Node-red msg.payload formula questions

Hello, @Biscuit
I was wondering if you might help me.
Been working for weeks scouring online docs/threads to move from total beginner to maybe intermediate in Node-red.
as I came across a thread (original post) and saw you seemed to understand well working with payloads from a current state node in Node-Red

I have a flow I imported (from a Hubitat forum) and in it, there is a function

I cannot for the life of me figure out how to get my sensor’s temp and humidity to be read into this function, which appears to expect two variables:
msg.payload.temperature.value
msg.payload.humidity.value

I couldn’t fully use his flow because his thermostat is in Hubitat, and i’m on Home Assistant. His original flow is bringing in an average of multiple humidistat sensors within Hubitat and i would like to potentially do something similar, but for the moment, I just chose one Sonoff humidistat and put 2 entity state nodes, one for each HA entity, to bring in the temperature and humidity.

I can go through a long list of things I’ve tried with error, for example, the debug mode always show the messages are an ‘object’ and so the function had told me that it can’t read the type ‘object’ so i set off trying change nodes, and JSON nodes trying to get the ‘type changed’

i just tried your msg.data.attributes suggestion:

and get this:

really lost here. any help is appreciated

I had someone say “change your formula” but i’d really like to understand the original formula, learn how to understand what i’m bringing in and how I might need to change it than to go off modifying that alot first.

As you asked so nicely :smile:

Starting with your function node.
This is presumably to calculate the dewpoint from an input temperature and humidity. All standard stuff, but as an aside the function uses ‘var’ which I would want to change to ‘let’ or ‘const’. Personally I would also have a go at turning this into JSONata (just because I like to move away from function nodes).

Yes, the function is extracing

msg.payload.temperature.value

so the expectation is that the message payload arrives as an object, something like

{"temperature": {"value": 18}, "humidity": {"value": 75}}

The complexity comes from the fact that this is a double-layer object, and we have to work backwards from
msg.payload.temperature.value to the starting object.

This is a fairly standard way of getting several values into and out of nodes by using an object.

So, what you want to do (I assume) is start with two msg.payload with one as temperature, and another as humidity.

Yes you can do this with the Change node. The trick is to set payload up as an object to start with. If msg.payload already contains something (which is usually does) then it will be of type number or string or Boolean, and we cannot add an object field to this (since it is not an object). However, if we first set payload as an object { } even an empty one, then we can add fields and values as we please.

Also we need to start with something other than payload, since we are going to pick another payload up later.

Step 1. - start with msg.payload as the temperature (I’m using an inject node to simulate this) and build a new msg.whatever as an object, and save the input payload to the temperature.value field.

This creates a new msg field as an empty object (so it is an object) and then I can add the temperature. Note that I can go straight from the empty object to a new field as an object itself - hence msg.whatever.temperature.value in one go.

Step 2. - simulate bringing in a second sensor value of the humidity

Step 3. - move the second value to the object, and then move the temporary msg.whatever over to msg.payload

And, you can see on the RHS the debug output shows msg.payload as the object that I think you want.

This is all a bit clunky and steps can be combined, so once you get the hang of this it is possible to go directly to
msg.payload set to {"temperature": {"value": payload }}

but you need JSONata to do that.

Note that the Change node can have more than one action, and these are carried out in sequence, so you can move payload to something then set payload to something else one after the other.

The ‘Deep copy value’ is not always necessary, but with objects (rather than primitives such as numbers) JavaScript copies by reference not value. This means that if you set msg.payload = msg.whatever then actually they point to the same object, so changing msg.whatever later will also change msg.payload (which causes problems). The deep copy actually creates a brand new object for the copy, so you have two different objects. Safer!

That will enable you to use the function node ‘as is’, and then you can look to simplify it later. Red-Green-Refactor (start with something that works, then change it to something better later).

Hope this helps…

1 Like

Thanks for your response. alot to process as I’m still super green, but it looks like a lot of information that can help and way better than alot of threads i happen upon

As you asked so nicely :smile: >

I hope it was, haha

This is presumably to calculate the dewpoint from an input temperature and humidity.

yes. (in the future, probably from an average of multiple sensors, for now, just chose one Sonoff sensor for this learning exercise)
I’m aware there is a few Home Assistant tools or template sensors etc that attempt to do Dew Point but I liked this guy’s reference formula (link) and his approach to manipulating the hvac thermostat with Node-red from this thread (link) (if anyone who finds this thread is interested more on the background).
And, I just really like Node-Red for some reason. The visual flows with nodes is really fun and makes alot of sense to me

{“temperature”: {“value”: 18}, “humidity”: {“value”: 75}}

I see, yes I was wondering how to get that value. It would be nice If I could do that right in my current_state nodes and send straight to the function if possible

This is a fairly standard way of getting several values into and out of nodes by using an object.

I was attempting to use a Join node and put the two into an Array (maybe that’s possible, maybe not necessary prior to the function, idk, but just something else I was trying based on something I saw in a myriad of other threads since probably December)

Yes you can do this with the Change node.

I’m open to using a simpler approach if you have an alternate suggestion, but that change node was just what I’d come across in another mostly unrelated thread

The trick is to set payload up as an object to start with. If msg.payload already contains something (which is usually does) then it will be of type number or string or Boolean, and we cannot add an object field to this (since it is not an object). However, if we first set payload as an object { } even an empty one, then we can add fields and values as we please.

Interesting.

So, what you want to do (I assume) is start with two msg.payload with one as temperature, and another as humidity.

These are the two current_state nodes I’m using (to replace his singular Hubitat node that I cannot view, even after installing hubitat in Node-Red). One brings in Temp and one brings in Humidity from two HA entities.

This is the properties

when I ran a debug straight off that current_state: it shows this


and i saw at the top it said msg: Object …

The trick is to set payload up as an object to start with.

so I assumed it was already an object

so once you get the hang of this it is possible to go directly to
msg.payload set to {"temperature": {"value": payload }}

but you need JSONata to do that.

I’d tried (with zero clue what I was doing) +add Output msg.topic = temperature (hoping to get that msg.payload.temperature.value mentioned above)
Tried State Type = String, number
Tried msg.payload = J: $number(payload)
but so, is it possible to use that directly in the HA current state nodes and skip the change nodes? I tried doing something before and got an illegal JSONata debug
but i’ve at least learned that JSONata is a thing, though a little fuzzy on the difference between JSONata and Javascript.

build a new msg.whatever as an object

I probably won’t have a chance to test until tomorrow afternoon your whatever.example (thanks for the pics) but that appears to be useful for the future that I can experiment with and also like you said here:

Note that the Change node can have more than one action, and these >are carried out in sequence, so you can move payload to something >then set payload to something else one after the other.

As for this:

uses ‘var’ which I would want to change to ‘let’ or ‘const’.
Personally I would also have a go at turning this into JSONata (just because I like to move away from function nodes)

I’d be interested to learn about that feel free to show an example
though I still want to follow his footsteps more first to see if I could get his flow going the way it was written; there’s more to it, as it publishes the output to MQTT and then comes back in from the MQTT topic to go into some flow.dewpoint (the whole flow is in the thread at the top if anyone is interested)

I am going to have to modify it some in the end anyways, because while my main HVAC is a heat pump, I also have a gas fireplace (with an esp32 trigger, currently running ) and a dehumifier (and i bought a zigbee relay for that) though I was able to get a second guy in that thread’s threshold info on how he incorporated a dehumidifier

It’s getting late. I will experiment some tomorrow afternoon after work. Hopefully this response was coherent haha

Node-RED is a great programming language. Visual and fast, it is easy to write ‘stuff’. However it still requires a fair bit of understanding of what is going on, particularly as it works quite differently to other computer languages.

There are a “million ways” to do what you want. Much is up to both knowledge and personal preference. It takes time to learn how to ‘write code’ and Node-RED is easy to get started but still takes time and effort to get up to speed with it.

Your post raises many points - too many to address comprehensively - but I will try and cover what I see as the critical ones.

Importing other code into Node-RED is a great way to learn and re-use, but does bring challenges, so I often break imported code down into bits and re-build, trying to understand and reworking as I go.

So, the task in hand is to take temperature and humidity and calculate the dew point (temperature).

You have two Current state nodes, where you can get the entity state value for the temperature and for the humidity. All (all?!) you have to do is take both values and perform a calculation on them, which is where the function node does its thing.

The message:
Node-RED uses messages that run between nodes, acting both as the flow of work and also as a carrier of data. As NR is written in JavaScript, the message is called msg and this is a JS object. As an object the message usually looks like:

{
    "_msgid" : "biglongrandomnumber",
    "payload": "60.4",
    "topic": ""
}

When you use a Debug node, if you set this to display msg.payload then you just get “60.4” but if you set to show complete message, you get everything. The _msgid is for NR internal use, and by convention we get payload and topic and possibly other stuff.

Here, msg.payload is just a string. The debug node is showing the entire msg and ‘object’ is because that is. Payload is not an object, but if it were the debug node would say ‘object’ against payload and offer the option to expand the object, just like the msg object in your picture.

The Current state node - output properties
By default these nodes set two outputs, msg.payload as the entity state which in your case is “60.4” and msg.data as entity which is the entire entity object. Since, by convention, nodes usually pass the ‘working data’ using msg.payload, most nodes expect msg.payload as both input and output. But you don’t have to stick to this rule.

How to get two values combined
If I just want ‘temperature’ I can use one Current state node, and set the msg.payload to the state value. Job done.

If I want temp and humidty, I have to get both. There are many ways to do this

  • chain two current state nodes in sequence
  • run two current state nodes side by side
  • do all the work in one current state node

are just three. To run in sequence, we have to save the first (temp) to something other than msg.payload (since the second will also try and do the same) so you can set one output property as

msg.temperature = entity state

and then, in the second Current state node

msg.humidity = entity state

and thus we have a message with msg.payload but also msg.temperature and msg.humidity. Easy! All we have to do is change the function node to expect these values in these fields.

To run side by side, yes we can use a Join node. This has to be set to manual mode, using either a count or time. This will create a nice object for you in msg.payload, as long as we have something like msg.topic to use as the keys. Therefore, if we setup the Current state node as

and the Join node as

then we will get an object in msg.payload with

{"temperature": 60.4, "humidity" 58}

And you can see that now msg.payload is an object! The Join node does all the work for you (as long as you set msg.topic in each Current state node…)

The state value

Worth point out that for the most part Home Assistant stores everything as strings, which means that the state value of the temperature sensor is “19.9” (I am in the UK hence Celsius). Since we want to work with numbers we need this as a number, and the easiest way is to let the Current state node do the work for you. Just select State Type as ‘number’ and it will convert.

The function node
Function nodes are great as a last resort for doing stuff there is no other node you can use, and in this case since we want Math.log from JS there is no other choice. I don’t have the time and patience to hand type your function node from a picture, but I found another one that does much the same thing.

const temp = msg.payload.temperature;
const humy = msg.payload.humidity;

const dewp = 243.04 * (Math.log(humy / 100) + ((17.625 * temp) / (243.04 + temp))) / (17.625 - Math.log(humy / 100) - ((17.625 * temp) / (243.04 + temp)));

msg.payload.dewpoint = Math.round(dewp*10)/10;

return msg;

I have changed a few things: first I am using msg.payload.temperature as this is now how my input message arrives (from the join node)

I have set the variables to const. Having spent a lot of time looking at how Node-RED and JS work with memory, I don’t use var as a) it is replaced by let and b) it holds memory space on the stack rather than the heap (which in English means it can hog memory and if you are not careful it can cause NR to run out of memory). const is even better as it saves even more memory.

I have cut the code down to the minimum - so it only accept Celsius, and I have pushed the output to msg.payload.dewpoint. Since I know that msg.payload is an object, I can take the opportunity of pushing the output as a new field in this object.

I had missed that I already have a temperature and a humidity sensor at home, so this has been a learning opportunity for me - and it works.

Checked with an on-line calculator, and the result agrees.

So I managed to knock up a flow in less time that it took to write this response ( :grin: ) which is why NR is great for prototyping.

Now we have to refactor to get it into a more usable shape.

Do I really want to trigger this every minutes?
No - only when either the temp or the humidity changes. So I use an Events: state node, with both sensors, and then this will fire when either changes value.

Do I really want to then re-read both sensors again?
No - since the Event: state node already has the details of the one that changed, I can use a bit of fancy JSONata to use the $entities() function and read the other.

I am not going to suggest that you use this, but I add this to show that you can save a lot of nodes with a little bit of code. Moot point as to whether you should. The JSONata code I have used here works out which sensor has fired the node, reads the other entity as well, and pulls both into one final object. You will have to change the code if you want to use it since this is very specific to the sensors I am using “sensor.temperature_and_humidity_sensor_humidity” requires looking at the end bit of the sensor id to work out that this is the ‘humidity’ sensor…

(
    $this_entity:=$entity().entity_id~>$substringAfter("sensor_");
    $this_state:=$entity().state~>$number();

    $other:= $this_entity="humidity" ?
        $entities('sensor.temperature_and_humidity_sensor_temperature') :
        $entities('sensor.temperature_and_humidity_sensor_humidity');

    $other_entity:=$other.entity_id~>$substringAfter("sensor_");
    $other_state:=$other.state~>$number();

    {$this_entity: $this_state, $other_entity: $other_state};

)

Note that I have set the Events node to ignore all previous and current unknown and unavailable states, and this means I will only get a trigger when the state value of either is valid and therefore I can convert this using $number() in the JSONata.

And, since it looks as if you are using Fahrenheit (the worst temperature scale ever invented) I have included a couple of Change nodes with the JSONata code required to do the conversions either way.

[{"id":"93d36ca368db3d74","type":"join","z":"dc25556591dc08be","name":"","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","useparts":false,"accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":590,"y":280,"wires":[["9175f0e15d169c73"]]},{"id":"1e24d5e768f0d39d","type":"api-current-state","z":"dc25556591dc08be","name":"Read Humidity","server":"","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.temperature_and_humidity_sensor_humidity","state_type":"num","blockInputOverrides":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"topic","propertyType":"msg","value":"humidity","valueType":"str"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":380,"y":320,"wires":[["93d36ca368db3d74"]]},{"id":"60a07948b37e282d","type":"api-current-state","z":"dc25556591dc08be","name":"Read Temperature","server":"","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.temperature_and_humidity_sensor_temperature","state_type":"num","blockInputOverrides":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"topic","propertyType":"msg","value":"temperature","valueType":"str"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":370,"y":260,"wires":[["93d36ca368db3d74"]]},{"id":"0e462da6ad93ba6a","type":"debug","z":"dc25556591dc08be","name":"debug 12","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":960,"y":280,"wires":[]},{"id":"20f8e844d833efa9","type":"inject","z":"dc25556591dc08be","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":170,"y":300,"wires":[["60a07948b37e282d","1e24d5e768f0d39d"]]},{"id":"9175f0e15d169c73","type":"function","z":"dc25556591dc08be","name":"function 2","func":"const temp = msg.payload.temperature;\nconst humy = msg.payload.humidity;\n\nconst dewp = 243.04 * (Math.log(humy / 100) + ((17.625 * temp) / (243.04 + temp))) / (17.625 - Math.log(humy / 100) - ((17.625 * temp) / (243.04 + temp)));\n\nmsg.payload.dewpoint = Math.round(dewp*10)/10;\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":780,"y":280,"wires":[["0e462da6ad93ba6a"]]},{"id":"8ca55e572063a253","type":"change","z":"dc25556591dc08be","name":"F2C","rules":[{"t":"set","p":"payload","pt":"msg","to":"(payload-32)/1.8","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":350,"y":440,"wires":[["026dbbb0dee3629e","d0c871adba0f1f1d"]]},{"id":"d0c871adba0f1f1d","type":"change","z":"dc25556591dc08be","name":"C2F","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload*1.8 + 32","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":650,"y":440,"wires":[["de91414ef0d10570"]]},{"id":"825a11037274b942","type":"inject","z":"dc25556591dc08be","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"72","payloadType":"num","x":170,"y":440,"wires":[["8ca55e572063a253"]]},{"id":"026dbbb0dee3629e","type":"debug","z":"dc25556591dc08be","name":"debug 13","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":500,"y":400,"wires":[]},{"id":"de91414ef0d10570","type":"debug","z":"dc25556591dc08be","name":"debug 14","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":820,"y":400,"wires":[]},{"id":"422b33fc38d72d8c","type":"server-state-changed","z":"dc25556591dc08be","name":"A change in T or H","server":"","version":6,"outputs":1,"exposeAsEntityConfig":"","entities":{"entity":["sensor.temperature_and_humidity_sensor_temperature","sensor.temperature_and_humidity_sensor_humidity"],"substring":[],"regex":[]},"outputInitially":true,"stateType":"num","ifState":"","ifStateType":"str","ifStateOperator":"is","outputOnlyOnStateChange":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":true,"ignorePrevStateUnknown":true,"ignorePrevStateUnavailable":true,"ignoreCurrentStateUnknown":true,"ignoreCurrentStateUnavailable":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"(\t    $this_entity:=$entity().entity_id~>$substringAfter(\"sensor_\");\t    $this_state:=$entity().state~>$number();\t\t    $other:= $this_entity=\"humidity\" ?\t        $entities('sensor.temperature_and_humidity_sensor_temperature') :\t        $entities('sensor.temperature_and_humidity_sensor_humidity');\t\t    $other_entity:=$other.entity_id~>$substringAfter(\"sensor_\");\t    $other_state:=$other.state~>$number();\t\t    {$this_entity: $this_state, $other_entity: $other_state};\t\t)","valueType":"jsonata"}],"x":250,"y":580,"wires":[["5625b94126a21314"]]},{"id":"7ebcf7384172b664","type":"debug","z":"dc25556591dc08be","name":"Dew-Point","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload.dewpoint","statusType":"msg","x":670,"y":540,"wires":[]},{"id":"5625b94126a21314","type":"function","z":"dc25556591dc08be","name":"function 3","func":"const temp = msg.payload.temperature;\nconst humy = msg.payload.humidity;\n\nconst dewp = 243.04 * (Math.log(humy / 100) + ((17.625 * temp) / (243.04 + temp))) / (17.625 - Math.log(humy / 100) - ((17.625 * temp) / (243.04 + temp)));\n\nmsg.payload.dewpoint = Math.round(dewp*10)/10;\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":480,"y":580,"wires":[["7ebcf7384172b664"]]}]

I leave you to experiment with this as you wish!

Firstly, thank you SO much for going through this. Everything you’re providing (custom) is amazing. It will probably take me… idk weeks or months to fully internalize it all haha. Nonetheless, it will be a valuable reference for me, thanks (and hopefully for anyone else who comes here later) and I feel Time is our most valuable commodity, so I thank you for yours.

I tweaked the current_state nodes with State Type to number and output properties etc like your example and removed the .value in his function and got it “working” now so it passes through to the next area of the flow. I’m getting the dewpoint through the Function which was the original ask in this thread so thanks. My main challenges now appear to be putting Home Assistant entity state and action nodes in place of his Hubitat nodes that check the state of and modify his HVAC thermostat

I am in the UK hence Celsius

Celsius? ew, gross (JK, understood. i think at least one of my sensors reports in C so i’ll need to maybe tackle something with that eventually as well)

I don’t have the time and patience to hand type your function node from a picture

BTW, Here’s the whole flow I’m working with that includes mostly his flow. He has some comment nodes, so maybe seeing it would help give some insight to why he designed it this way

[{"id":"6f7419f5ff19fe94","type":"tab","label":"Flow 2","disabled":false,"info":""},{"id":"d08b7c923494f3f9","type":"function","z":"6f7419f5ff19fe94","name":"Dew-point calculator","func":"function f2c(F) {\nvar C = 5 * ((F-32)/9);\nreturn C;\n}\nfunction dpC(T,RH) {\nvar Td = 243.04 * (Math.log(RH/100)+((17.625*T)/(243.04+T)))/(17.625-Math.log(RH/100)-((17.625*T)/(243.04+T)));\nreturn Td;\n}\nfunction c2f(C) {\nvar F = parseFloat((32 + (9*(C/5))).toFixed(2));\nreturn F;\n}\nvar tempf = msg.payload.temperature;\nvar rel_hum = msg.payload.humidity;\nvar tempc = f2c(tempf);\nvar dewpointc = dpC(tempc,rel_hum);\nmsg.payload = c2f(dewpointc);\nmsg.topic = \"dewpoint\";\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1020,"y":400,"wires":[["ec3316c8e6a0bb27"]]},{"id":"ec3316c8e6a0bb27","type":"rbe","z":"6f7419f5ff19fe94","name":"","func":"rbe","gap":"","start":"","inout":"out","property":"payload","x":1165,"y":400,"wires":[["9cb9482b5b22bc69","d5f1d5bc6ff1753a","aa608f7d118740a0"]],"l":false},{"id":"9cb9482b5b22bc69","type":"mqtt out","z":"6f7419f5ff19fe94","name":"Indoor Dewpoint","topic":"dewpoint","qos":"2","retain":"true","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"40144fb8.a1c1e","x":1320,"y":400,"wires":[]},{"id":"3280f38a9bc33e04","type":"comment","z":"6f7419f5ff19fe94","name":"Using cron, calculate the current dew-point (flow.dewpoint) every minute","info":"","x":310,"y":320,"wires":[]},{"id":"d0d209f747b61e3d","type":"mqtt in","z":"6f7419f5ff19fe94","name":"Indoor Dewpoint","topic":"dewpoint","qos":"2","datatype":"auto","broker":"40144fb8.a1c1e","inputs":0,"x":120,"y":620,"wires":[["f183971c76265fd8","099a2079d8cb7081"]]},{"id":"fb4fd47f7ba4f84e","type":"switch","z":"6f7419f5ff19fe94","name":"cool only","property":"thermostat_mode","propertyType":"flow","rules":[{"t":"eq","v":"cool","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":480,"y":620,"wires":[["78a33abb18560a07"]]},{"id":"78a33abb18560a07","type":"switch","z":"6f7419f5ff19fe94","name":"dewpoint > Td","property":"dewpoint","propertyType":"flow","rules":[{"t":"gt","v":"Td","vt":"flow"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":660,"y":620,"wires":[["a8a48b96f5caac2b"],["fe7df28085cc524d"]]},{"id":"a8a48b96f5caac2b","type":"delay","z":"6f7419f5ff19fe94","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"minutes","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"outputs":1,"x":835,"y":620,"wires":[["ca420f3621f55396"]],"l":false},{"id":"fe7df28085cc524d","type":"change","z":"6f7419f5ff19fe94","name":"reset","rules":[{"t":"set","p":"reset","pt":"msg","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":790,"y":680,"wires":[["a8a48b96f5caac2b"]]},{"id":"ca420f3621f55396","type":"switch","z":"6f7419f5ff19fe94","name":"thermostatOperatingState = idle","property":"thermostatOperatingState","propertyType":"flow","rules":[{"t":"eq","v":"idle","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":1030,"y":620,"wires":[["222d5f659d7aa019"]]},{"id":"222d5f659d7aa019","type":"delay","z":"6f7419f5ff19fe94","name":"","pauseType":"delay","timeout":"20","timeoutUnits":"seconds","rate":"1","nbRateUnits":"5","rateUnits":"minute","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"outputs":1,"x":1215,"y":620,"wires":[["d0ca51800eb00a93"]],"l":false},{"id":"d0ca51800eb00a93","type":"change","z":"6f7419f5ff19fe94","name":"command = setCoolingSetpoint; msg.arguments = flow.thermostatTemp - 3","rules":[{"t":"set","p":"command","pt":"msg","to":"setCoolingSetpoint","tot":"str"},{"t":"set","p":"arguments","pt":"msg","to":"$flowContext(\"thermostatTemp\") - 3","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1550,"y":620,"wires":[["8a7488e0749bb8b1"]]},{"id":"8a7488e0749bb8b1","type":"delay","z":"6f7419f5ff19fe94","name":"","pauseType":"delay","timeout":"2","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"outputs":1,"x":1875,"y":620,"wires":[["c44b1fe86429eb63"]],"l":false},{"id":"94af3707402283e7","type":"comment","z":"6f7419f5ff19fe94","name":"In cool mode, if the dewpoint is > flow.Td, and the thermostat operating state is idle, then setCoolingSetpoint to be 3 degrees less than the current thermostat temperature","info":"","x":610,"y":500,"wires":[]},{"id":"dbfb6df5b3850d2c","type":"comment","z":"6f7419f5ff19fe94","name":"On purpose, there is a 1 minute resetable delay between dewpoint > flow.td and proceeding with the sequence","info":"","x":420,"y":560,"wires":[]},{"id":"f183971c76265fd8","type":"change","z":"6f7419f5ff19fe94","name":"toNumber","rules":[{"t":"set","p":"payload","pt":"msg","to":"$number(payload)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":620,"wires":[["fb4fd47f7ba4f84e"]]},{"id":"a9fa4a5d2256af69","type":"switch","z":"6f7419f5ff19fe94","name":"cooling","property":"payload.value","propertyType":"msg","rules":[{"t":"eq","v":"cooling","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":320,"y":920,"wires":[["f41ecc8567d19bcf"]]},{"id":"261f3b2f1734164c","type":"switch","z":"6f7419f5ff19fe94","name":"msg.payload <= flow.Td","property":"payload","propertyType":"msg","rules":[{"t":"lte","v":"Td","vt":"flow"}],"checkall":"true","repair":false,"outputs":1,"x":990,"y":920,"wires":[["6fb9492389614d29"]]},{"id":"f41ecc8567d19bcf","type":"change","z":"6f7419f5ff19fe94","name":"payload = flow.dewpoint; payload = payload + 2.5","rules":[{"t":"set","p":"payload","pt":"msg","to":"dewpoint","tot":"flow"},{"t":"set","p":"payload","pt":"msg","to":"$number(payload) + 2.5","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":630,"y":920,"wires":[["d5b348cd23f79c1c","261f3b2f1734164c"]]},{"id":"d5b348cd23f79c1c","type":"delay","z":"6f7419f5ff19fe94","name":"1 min","pauseType":"delay","timeout":"1","timeoutUnits":"minutes","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"outputs":1,"x":450,"y":840,"wires":[["8f8ff053e1487800"]]},{"id":"8f8ff053e1487800","type":"switch","z":"6f7419f5ff19fe94","name":"thermostatOperatingState = cooling","property":"thermostatOperatingState","propertyType":"global","rules":[{"t":"eq","v":"cooling","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":690,"y":840,"wires":[["f41ecc8567d19bcf"]]},{"id":"d5f1d5bc6ff1753a","type":"change","z":"6f7419f5ff19fe94","name":"flow.dewpoint","rules":[{"t":"set","p":"dewpoint","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1320,"y":460,"wires":[["aa608f7d118740a0"]]},{"id":"6fb9492389614d29","type":"change","z":"6f7419f5ff19fe94","name":"command = setCoolingSetpoint; msg.arguments = flow.thermostatTemp + 3","rules":[{"t":"set","p":"command","pt":"msg","to":"setCoolingSetpoint","tot":"str"},{"t":"set","p":"arguments","pt":"msg","to":"$flowContext(\"thermostatTemp\") + 3","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1410,"y":920,"wires":[["a56ba10e11fb111b"]]},{"id":"a56ba10e11fb111b","type":"delay","z":"6f7419f5ff19fe94","name":"","pauseType":"delay","timeout":"2","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"outputs":1,"x":1735,"y":920,"wires":[["d5659f38c2a90522"]],"l":false},{"id":"e594516cc2d95641","type":"comment","z":"6f7419f5ff19fe94","name":"Keep cooling until \"flow.dewpoint + 2.5\" <= flow.Td, then raise the thermostat temperature by 3 degrees to turn off cooling","info":"","x":460,"y":760,"wires":[]},{"id":"a3fcf96654c2b8fe","type":"change","z":"6f7419f5ff19fe94","name":"flow.Td=56.0 (Sleep)","rules":[{"t":"set","p":"Td","pt":"flow","to":"56","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":760,"y":120,"wires":[["fea3312c8047c6e0"]]},{"id":"6f258c2811e969bb","type":"change","z":"6f7419f5ff19fe94","name":"flow.Td=57.5 (Home)","rules":[{"t":"set","p":"Td","pt":"flow","to":"57.5","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":760,"y":180,"wires":[["fea3312c8047c6e0"]]},{"id":"8309f88bb4b738a8","type":"change","z":"6f7419f5ff19fe94","name":"flow.Td=61.0 (Away)","rules":[{"t":"set","p":"Td","pt":"flow","to":"61","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":760,"y":240,"wires":[["fea3312c8047c6e0"]]},{"id":"cdd8bd495864dc1d","type":"switch","z":"6f7419f5ff19fe94","name":"Sleep/Home/Away","property":"payload.value","propertyType":"msg","rules":[{"t":"eq","v":"sleep","vt":"str"},{"t":"eq","v":"home","vt":"str"},{"t":"eq","v":"away","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":510,"y":200,"wires":[["a3fcf96654c2b8fe"],["6f258c2811e969bb"],["8309f88bb4b738a8"]]},{"id":"0b41f3fb9772a40d","type":"comment","z":"6f7419f5ff19fe94","name":"Set the desired dewpoint (flow.Td) based on Mode","info":"","x":250,"y":100,"wires":[]},{"id":"e8324d8f0f2ba42a","type":"hubitat device","z":"6f7419f5ff19fe94","name":"Honeywell T6 Pro","server":"662851c4.3ccad","deviceId":"3493","attribute":"thermostatOperatingState","sendEvent":true,"x":130,"y":920,"wires":[["a9fa4a5d2256af69"]]},{"id":"c44b1fe86429eb63","type":"hubitat command","z":"6f7419f5ff19fe94","deviceLabel":"Honeywell T6 Pro","name":"","server":"662851c4.3ccad","deviceId":"3493","command":"","commandArgs":"","x":2030,"y":620,"wires":[[]]},{"id":"d5659f38c2a90522","type":"api-call-service","z":"6f7419f5ff19fe94","name":"","server":"db91e348.88bee","version":7,"debugenabled":false,"action":"climate.set_hvac_mode","floorId":[],"areaId":[],"deviceId":[],"entityId":["climate.home"],"labelId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"climate","service":"set_hvac_mode","x":1950,"y":920,"wires":[[]]},{"id":"inject","type":"inject","z":"6f7419f5ff19fe94","name":"Trigger","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":"","topic":"","payload":"74","payloadType":"num","x":230,"y":1140,"wires":[["03bbd9bc0c8fc05c"]]},{"id":"03bbd9bc0c8fc05c","type":"api-call-service","z":"6f7419f5ff19fe94","name":"","server":"db91e348.88bee","version":7,"debugenabled":false,"action":"climate.set_temperature","floorId":[],"areaId":[],"deviceId":[],"entityId":["climate.home"],"labelId":[],"data":"{\t    \"target_temp_high\":76,\t    \"target_temp_low\": 74\t}\t","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"climate","service":"set_temperature","x":510,"y":1140,"wires":[[]]},{"id":"53b8bc17ddb72516","type":"server-state-changed","z":"6f7419f5ff19fe94","name":"","server":"db91e348.88bee","version":6,"outputs":1,"exposeAsEntityConfig":"","entities":{"entity":["select.home_current_mode"],"substring":[],"regex":[]},"outputInitially":false,"stateType":"str","ifState":"","ifStateType":"str","ifStateOperator":"is","outputOnlyOnStateChange":false,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":200,"y":240,"wires":[["cdd8bd495864dc1d","e66a9538690a282f"]]},{"id":"fea3312c8047c6e0","type":"debug","z":"6f7419f5ff19fe94","name":"debug 11","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1020,"y":160,"wires":[]},{"id":"9ed57a06e468a6c0","type":"api-current-state","z":"6f7419f5ff19fe94","name":"","server":"db91e348.88bee","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.sonoff_humidistat_temperature","state_type":"num","blockInputOverrides":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"topic","propertyType":"msg","value":"temperature","valueType":"str"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":460,"y":400,"wires":[["783151285da3a70a"]]},{"id":"9a30405301a428ad","type":"api-current-state","z":"6f7419f5ff19fe94","name":"","server":"db91e348.88bee","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.sonoff_humidistat_humidity","state_type":"num","blockInputOverrides":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"topic","propertyType":"msg","value":"humidity","valueType":"str"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":450,"y":440,"wires":[["83416fa2c1f2d3e8"]]},{"id":"83416fa2c1f2d3e8","type":"delay","z":"6f7419f5ff19fe94","name":"25ms","pauseType":"delay","timeout":"25","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"outputs":1,"x":695,"y":460,"wires":[["783151285da3a70a"]],"l":false},{"id":"2eaba0a5a127c8ad","type":"inject","z":"6f7419f5ff19fe94","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":120,"y":440,"wires":[["9ed57a06e468a6c0","9a30405301a428ad"]]},{"id":"099a2079d8cb7081","type":"debug","z":"6f7419f5ff19fe94","name":"debug 7","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":320,"y":700,"wires":[]},{"id":"66d1177a20fe4112","type":"cronplus","z":"6f7419f5ff19fe94","name":"","outputField":"payload","timeZone":"America/New_York","storeName":"","commandResponseMsgOutput":"output1","defaultLocation":"37.061977477860395 -76.49204134941101","defaultLocationType":"fixed","outputs":1,"options":[{"name":"schedule1","topic":"schedule1","payloadType":"bool","payload":"true","expressionType":"cron","expression":"0 * * * * *","location":"","offset":"0","solarType":"selected","solarEvents":"nauticalDusk"}],"x":100,"y":360,"wires":[["9ed57a06e468a6c0","9a30405301a428ad"]]},{"id":"93d36ca368db3d74","type":"join","z":"6f7419f5ff19fe94","name":"","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","useparts":false,"accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":670,"y":1780,"wires":[["9175f0e15d169c73"]]},{"id":"1e24d5e768f0d39d","type":"api-current-state","z":"6f7419f5ff19fe94","name":"Read Humidity","server":"db91e348.88bee","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.sonoff_humidistat_humidity","state_type":"num","blockInputOverrides":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"topic","propertyType":"msg","value":"humidity","valueType":"str"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":460,"y":1820,"wires":[["93d36ca368db3d74"]]},{"id":"60a07948b37e282d","type":"api-current-state","z":"6f7419f5ff19fe94","name":"Read Temperature","server":"db91e348.88bee","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.sonoff_humidistat_temperature","state_type":"num","blockInputOverrides":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"topic","propertyType":"msg","value":"temperature","valueType":"str"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":450,"y":1760,"wires":[["93d36ca368db3d74"]]},{"id":"0e462da6ad93ba6a","type":"debug","z":"6f7419f5ff19fe94","name":"debug 12","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1040,"y":1780,"wires":[]},{"id":"20f8e844d833efa9","type":"inject","z":"6f7419f5ff19fe94","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":250,"y":1800,"wires":[["60a07948b37e282d","1e24d5e768f0d39d"]]},{"id":"9175f0e15d169c73","type":"function","z":"6f7419f5ff19fe94","name":"function 2","func":"const temp = msg.payload.temperature;\nconst humy = msg.payload.humidity;\n\nconst dewp = 243.04 * (Math.log(humy / 100) + ((17.625 * temp) / (243.04 + temp))) / (17.625 - Math.log(humy / 100) - ((17.625 * temp) / (243.04 + temp)));\n\nmsg.payload.dewpoint = Math.round(dewp*10)/10;\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":860,"y":1780,"wires":[["0e462da6ad93ba6a"]]},{"id":"8ca55e572063a253","type":"change","z":"6f7419f5ff19fe94","name":"F2C","rules":[{"t":"set","p":"payload","pt":"msg","to":"(payload-32)/1.8","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":1940,"wires":[["026dbbb0dee3629e","d0c871adba0f1f1d"]]},{"id":"d0c871adba0f1f1d","type":"change","z":"6f7419f5ff19fe94","name":"C2F","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload*1.8 + 32","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":730,"y":1940,"wires":[["de91414ef0d10570"]]},{"id":"825a11037274b942","type":"inject","z":"6f7419f5ff19fe94","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"72","payloadType":"num","x":250,"y":1940,"wires":[["8ca55e572063a253"]]},{"id":"026dbbb0dee3629e","type":"debug","z":"6f7419f5ff19fe94","name":"debug 13","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":580,"y":1900,"wires":[]},{"id":"de91414ef0d10570","type":"debug","z":"6f7419f5ff19fe94","name":"debug 14","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":900,"y":1900,"wires":[]},{"id":"422b33fc38d72d8c","type":"server-state-changed","z":"6f7419f5ff19fe94","name":"A change in T or H","server":"db91e348.88bee","version":6,"outputs":1,"exposeAsEntityConfig":"","entities":{"entity":["sensor.sonoff_humidistat_temperature","sensor.sonoff_humidistat_humidity"],"substring":[],"regex":[]},"outputInitially":true,"stateType":"num","ifState":"","ifStateType":"str","ifStateOperator":"is","outputOnlyOnStateChange":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":true,"ignorePrevStateUnknown":true,"ignorePrevStateUnavailable":true,"ignoreCurrentStateUnknown":true,"ignoreCurrentStateUnavailable":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"(\t    $this_entity:=$entity().entity_id~>$substringAfter(\"sensor_\");\t    $this_state:=$entity().state~>$number();\t\t    $other:= $this_entity=\"humidity\" ?\t        $entities('sensor.temperature_and_humidity_sensor_temperature') :\t        $entities('sensor.temperature_and_humidity_sensor_humidity');\t\t    $other_entity:=$other.entity_id~>$substringAfter(\"sensor_\");\t    $other_state:=$other.state~>$number();\t\t    {$this_entity: $this_state, $other_entity: $other_state};\t\t)","valueType":"jsonata"}],"x":330,"y":2080,"wires":[["5625b94126a21314"]]},{"id":"7ebcf7384172b664","type":"debug","z":"6f7419f5ff19fe94","name":"Dew-Point","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload.dewpoint","statusType":"msg","x":750,"y":2040,"wires":[]},{"id":"5625b94126a21314","type":"function","z":"6f7419f5ff19fe94","name":"function 3","func":"const temp = msg.payload.temperature;\nconst humy = msg.payload.humidity;\n\nconst dewp = 243.04 * (Math.log(humy / 100) + ((17.625 * temp) / (243.04 + temp))) / (17.625 - Math.log(humy / 100) - ((17.625 * temp) / (243.04 + temp)));\n\nmsg.payload.dewpoint = Math.round(dewp*10)/10;\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":560,"y":2080,"wires":[["7ebcf7384172b664"]]},{"id":"577a14aa57e178a2","type":"comment","z":"6f7419f5ff19fe94","name":"Everything below this is flows I've imported to fiddle around with and learn from and possibly move up into the original","info":"","x":430,"y":1060,"wires":[]},{"id":"783151285da3a70a","type":"join","z":"6f7419f5ff19fe94","name":"","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","useparts":false,"accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":830,"y":400,"wires":[["d08b7c923494f3f9"]]},{"id":"a4481fb014182de7","type":"api-call-service","z":"6f7419f5ff19fe94","name":"","server":"db91e348.88bee","version":7,"debugenabled":false,"action":"climate.set_temperature","floorId":[],"areaId":[],"deviceId":[],"entityId":["climate.home"],"labelId":[],"data":"{\t    \"target_temp_high\":76,\t    \"target_temp_low\": 74\t}\t","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","blockInputOverrides":true,"domain":"climate","service":"set_temperature","x":2050,"y":700,"wires":[[]]},{"id":"e3109eb05f64b920","type":"comment","z":"6f7419f5ff19fe94","name":"*Can't see what his Honeywell Hubitat node is doing here","info":"","x":2150,"y":580,"wires":[]},{"id":"cea2fe12e3fa0594","type":"comment","z":"6f7419f5ff19fe94","name":"*i need to swap in a Home Assistant version for my hvac","info":"","x":2140,"y":660,"wires":[]},{"id":"3d4e7b0a19894d5c","type":"comment","z":"6f7419f5ff19fe94","name":"*I'm open to the changed state node, but for now will keep his cron method","info":"","x":480,"y":360,"wires":[]},{"id":"380f9485deb5e7b7","type":"hubitat mode","z":"6f7419f5ff19fe94","name":"Mode","server":"5931aca0a5725c31","sendEvent":true,"x":250,"y":140,"wires":[[]]},{"id":"e66a9538690a282f","type":"debug","z":"6f7419f5ff19fe94","name":"debug 10","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":460,"y":140,"wires":[]},{"id":"f8d2f6e956c6ed38","type":"comment","z":"6f7419f5ff19fe94","name":"*Need to replace his \"Mode\" hubitat node","info":"","x":200,"y":200,"wires":[]},{"id":"aa608f7d118740a0","type":"debug","z":"6f7419f5ff19fe94","name":"debug 15","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1500,"y":460,"wires":[]},{"id":"5cbe81062e10711c","type":"comment","z":"6f7419f5ff19fe94","name":"*Need to replace his \"Honeywell\" hubitat node","info":"","x":190,"y":960,"wires":[]},{"id":"d8489b6c61fb83ce","type":"comment","z":"6f7419f5ff19fe94","name":"*Need to replace his \"Honeywell\" hubitat node","info":"","x":1990,"y":880,"wires":[]},{"id":"d628e941517bdb82","type":"hubitat command","z":"6f7419f5ff19fe94","deviceLabel":"Honeywell T6 Pro","name":"","server":"08dea297ac72227c","deviceId":"3493","command":"","commandArgs":"","x":1950,"y":840,"wires":[[]]},{"id":"361ee7d0f638c63e","type":"api-current-state","z":"6f7419f5ff19fe94","name":"","server":"db91e348.88bee","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.sonoff_humidistat_humidity","state_type":"num","blockInputOverrides":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"topic","propertyType":"msg","value":"humidity","valueType":"str"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":190,"y":1000,"wires":[[]]},{"id":"40144fb8.a1c1e","type":"mqtt-broker","name":"General","broker":"192.168.107.66","port":"1883","tls":"","clientid":"","autoConnect":true,"usetls":false,"compatmode":false,"protocolVersion":4,"keepalive":"15","cleansession":true,"autoUnsubscribe":true,"birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":"","birthMsg":{},"closeTopic":"","closeRetain":"false","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willRetain":"false","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""},{"id":"db91e348.88bee","type":"server","name":"Home Assistant1","addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"","connectionDelay":false,"cacheJson":false,"heartbeat":false,"heartbeatInterval":"","statusSeparator":"","enableGlobalContextStore":false},{"id":"5931aca0a5725c31","type":"hubitat config","name":"HubitatM","usetls":false,"host":"192.168.1.46","port":"80","appId":"1522","nodeRedServer":"http://192.168.1.4:1880","webhookPath":"/hubitat/webhook","autoRefresh":true,"useWebsocket":false,"colorEnabled":true,"color":"#5dd049"},{"id":"08dea297ac72227c","type":"hubitat config","name":"HubitatS","usetls":false,"host":"192.168.1.36","port":"80","appId":"4489","nodeRedServer":"http://192.168.1.4:1880","webhookPath":"/hubitat/webhook2","autoRefresh":true,"useWebsocket":false,"colorEnabled":true,"color":"#51b8f0"}]

I imported your code to test quickly before going to work
(after swapping in my entities) it mostly worked, except the last debug also shows NaN

Do I really want to trigger this every minutes?
No - only when either the temp or the humidity changes. So I use an Events: state node, with both sensors, and then this will fire when either changes value.

yeah, idk that it really needs to be done every minute, probably a state_changed node would be fine, but maybe the Hubitat nodes don’t have something like the Event:state Home Assistant changed Node, as if you look in the flow i just sent, he had a filter after the Function that says “block unless value changes” so maybe that’s how he has to monitor a state change with Hubitat and NR…
But I was already poking around at using a state changed node elsewhere;
in the flow i was attempting to swap in a state changed node for his Hubitat node which tries to Set a “flow.Td” (Node-red variable?) depending on the Home/Sleep/Away “mode” of the HVAC, but I’ve had issues with that as well, as I need to access these HA Entity attributes somehow (from my Ecobee Thermostat entity), I can see the attributes in the Developer Tools, but they aren’t found as an Entity in Node-Red, so i’ll need to figure out how to get that attribute in the Events:state node

I did find out I can’t modify the Home/Away/Sleep mode on an Ecobee Thermostat (github thread) but i would think I could read the states from the attributes at least.
I know that the test i did in the flow down near the bottom that uses JSONata (i stumbled on that somewhere):


worked for modifying my thermostat target temperate range but i’m not sure how to read in the “hvac_modes” attribute from the current state node. Would that be done with JSONata also?

It is only when looking at a ‘simple’ task like this that it becomes apparent quite how much we have to know to be able to connect Node-RED and Home Assistant. Node-RED is a new way of doing things, and HA works differently, so to solve even simple problems requires quite a breadth and depth of knowledge.

I run the ‘risk’ of having to explain all of HA and NR to solve this one. However:-

Had a look at the flow. When importing you should try to remove the included HA server configuration node (importing these can lead to problems) so the scrubber is a good tool to use. As I (now) know what I am doing I can hand-edit and remove the HA server node out of the import window, but the scrubber is there if you need it.

For myself, in such cases I like to step back and work out what is going on and the logic of the flow. I assume that, given an input setting of sleep/home/away the object is to set a target dewpoint based on ‘mode’, monitor the current dew-point (every so often), and adjust various HVAC settings to drive humidity to this target value.

From this I would work out what I want to control, what I need to monitor, what data flow I want, and then how to put it all together.

In Home Assistant we have ‘input’ sensors, which can be manually adjusted from the dashboard. Use the Helper to add sensors like an input number (with slider) and you have the means to set target value, or use an input selector list for ‘mode’. These input-whatever can be easily read in NR using a current state node.

In Node-RED we have ‘context’ which is memory (usually volatile) that can be used to hold values. Useful since ‘data’ only lasts as long as the flow message does. Context comes as node/flow/global and the usual one to to use is flow. These can be set and read from function nodes, but also Change nodes. Easy therefore to have a flow running that just monitors the humidity and temperature, calculates the dew point, and saves this to NR flow and/or HA (sensor). Then other flows can read this back when they need it.

HA consists of entities. These are ‘things’ and they all have ‘states’ which is the primary value we normally use. My temp-humidity device is added to HA with an integration, and this integration has created two sensors, one for temp and the other for humidity. These are read-only, and I have to read both sensors to get both values, as per out discussions above.

Simple things just have ‘states’. More complicated things have ‘attributes’. There are extra values that hold more information, and can be read but require a bit more effort, since HA is mostly setup to work with sensor.state as a single primitive value (string, number, boolean).

Climate devices - HVAC - are much more interesting, and will always have a bunch of attributes

All entities are added to HA (well, almost all) by ‘integrations’ which create the entity, manage the entity, and keep the state value updated from the real world. Along side this, some integrations will add ‘services’, now called Actions, that can be used to modify the entity. HA will not allow anything to change an entity state/attribute other than the integration that created it. The exceptions to this rule are - if you use the WebSocket nodes then Node-RED is the creating integration and you can change the state/attribute. - if you use ‘input.whatevers’ then you can add these to HA dashboard, and manually change them. - and Actions can be used to change entities, depending on the range of Actions that the integration has added for you.

For climate, therefore, your climate.home device has a state value of the mode “heat_cool” but a bunch of attributes. Some will be read only, some will be write-to.

I note that your attributes include current_temperature and current_humidity, which suggests that you could read both of these in one go. The Current state node and the Events: state node will pull the entire entity object, which includes the state and all the attributes.

In a Current state node, the default ‘output properties’ includes setting msg.data to entity and after the node (not in it) you can read msg.data.attributes.current_temperature (and humidity) and use these values directly in the function node to calculate dewpoint.

Your ‘hvac_modes’ attribute can be read as data.attributes.hvac_modes which will give you the list of possible modes (not the actual mode set - that is the entity state value).

To change this setting you need to find the Action that will permit this. Use HA Developer Tools > Actions and find “Climate.Set HVAC mode”. Once you can run the Action from HA you can use the Action node in Node-RED to do the same. Various settings you can change will be things like mode and target temperature.

How to use Actions nodes is an entire chapter in a book in itself, but you can find examples in the HA WebSocket node examples and cookbook.

I hope this helps!

1 Like