Homekit in nodeRED to control a color bulb attached to openHAB

I’d like to control a color bulb attached in openHAB via a homekit connection in nodeRED. Has anyone already achieved this? Potentially with a function node? Can you provide the nodeRED code?

The main reason to do the integration in nodeRED is that the homekit color picker with the actual homekit integration in openHAB is not usable.

The connection between openHAB and nodeRED is established via the REST API of openHAB an not with MQTT.

Hi,

I tried to achieve this and made a function node before and after the homekit-node

The function node before the homekit-node has this:

var results = msg.payload.split(',');
if (results[2] < 1) {
   OnOff = false;
} else {
   OnOff = true;
}
msg.payload = {
    "Brightness": results[2],
    "Hue": results[0],
    "Saturation": results[1],
    "On": OnOff
        };
return msg;

What is actually should do is taking the HSB-Value we get from the color-item and split it to it’s different values and create the objects Brightness Hue and Saturation in the desired format for the homekit-node.
Also to represent the on/off-state if brightness is not zero the bulb is on.

After the homekit-node I created a function node with this content:

var h = context.get('h')||0;
var s = context.get('s')||0;
var b = context.get('b')||0;

if(msg.payload.On === false){
        h = 0;
        context.set('h',h)
        s = 0;
        context.set('s',s)
        b = 0;
        context.set('b',b)
}
if(msg.payload.Hue){
    h = msg.payload.Hue;
        context.set('h',h)
}
if(msg.payload.Saturation){
    s = msg.payload.Saturation;
        context.set('s',s)
}
if(msg.payload.Brightness){
    b = msg.payload.Brightness;
        context.set('b',b)
}
msg.payload = h+","+s+","+b;
if (msg.hap.context !== undefined )
{
return msg;
}

The homekit-node puts out the objects Hue, Saturation and Brightness individually but for sending a h,s,b-String we need all values. So I thought of the “persisting” variables in the node with this context-thing.

So when initializing the node create h, s and b with “0”

Then if homekit puts out “On: false” (so literally off) send 0,0,0.
Then when a value of Saturation, Hue or Brightness arrives set the respective variable to that value and take the other values from the previous run so we can send out a complete h,s,b-value every time to our olor-item.
At last as in the other thread mentioned to prevent loops when setting the node in between the color-item out and in check if the changes are coming from the homekit-node and only then send the payload.

Please keep in mind that I don’t really know what I am doing here with javascript but it seems to work.
If someone can optimize that or rewrite it in a better way you are very welcome.

HTH

Nico

2 Likes

hi. Great. will try asap. Can you provide the nodered code here too?
best,
simon

Basically it’s nothing complicated, just paste the code in function nodes and connect them.

I made a basic flow here with a openhab-node in, function - homekit - function - openhab out.

The nodes have to be configured of course so you have to put an RGB-item on the in and out-node.

[
    {
        "id": "64bead44.4144c4",
        "type": "function",
        "z": "215cec59.1d50f4",
        "name": "HSB2Homekit",
        "func": "var results = msg.payload.split(',');\nif (results[2] < 1) {\n   OnOff = false;\n} else {\n   OnOff = true;\n}\nmsg.payload = {\n    \"Brightness\": results[2],\n    \"Hue\": results[0],\n    \"Saturation\": results[1],\n    \"On\": OnOff\n        };\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 360,
        "y": 140,
        "wires": [
            [
                "b74e9a11.69c4f8"
            ]
        ]
    },
    {
        "id": "7d241b5a.e66f94",
        "type": "function",
        "z": "215cec59.1d50f4",
        "name": "HomeKit2HSB",
        "func": "var h = context.get('h')||0;\nvar s = context.get('s')||0;\nvar b = context.get('b')||0;\n\nif(msg.payload.On === false){\n        h = 0;\n        context.set('h',h)\n        s = 0;\n        context.set('s',s)\n        b = 0;\n        context.set('b',b)\n}\nif(msg.payload.Hue){\n    h = msg.payload.Hue;\n        context.set('h',h)\n}\nif(msg.payload.Saturation){\n    s = msg.payload.Saturation;\n        context.set('s',s)\n}\nif(msg.payload.Brightness){\n    b = msg.payload.Brightness;\n        context.set('b',b)\n}\nmsg.payload = h+\",\"+s+\",\"+b;\nif (msg.hap.context !== undefined )\n{\nreturn msg;\n}",
        "outputs": 1,
        "noerr": 0,
        "x": 740,
        "y": 140,
        "wires": [
            [
                "1acbeeaa.01d6d1"
            ]
        ]
    },
    {
        "id": "eef9c664.019a38",
        "type": "openhab2-in",
        "z": "215cec59.1d50f4",
        "name": "",
        "controller": "",
        "itemname": "",
        "x": 140,
        "y": 140,
        "wires": [
            [
                "64bead44.4144c4"
            ],
            []
        ]
    },
    {
        "id": "1acbeeaa.01d6d1",
        "type": "openhab2-out",
        "z": "215cec59.1d50f4",
        "name": "",
        "controller": "",
        "itemname": "",
        "topic": "",
        "payload": "",
        "x": 980,
        "y": 140,
        "wires": []
    },
    {
        "id": "b74e9a11.69c4f8",
        "type": "homekit-service",
        "z": "215cec59.1d50f4",
        "accessory": "",
        "name": "",
        "serviceName": "",
        "x": 540,
        "y": 140,
        "wires": [
            [
                "7d241b5a.e66f94"
            ]
        ]
    }
]

Try it out if it works!

@nibblerrick - I just spent about a day and a half looking for functions like this. It looks VERY close to what I need (I don’t have color changing lights so I’ll remove hue and saturation).

So my functions would be:

var results = msg.payload;
if (results < 1) {
   OnOff = false;
} else {
   OnOff = true;
}
msg.payload = {
    "Brightness": results,
    "On": OnOff
        };
return msg;

and:

var b = context.get('b')||0;

if(msg.payload.On === false){
        b = 0;
        context.set('b',b)
}
if(msg.payload.Brightness){
    b = msg.payload.Brightness;
        context.set('b',b)
}
msg.payload = b;
if (msg.hap.context !== undefined )
{
return msg;
}

I haven’t tried any of this - it may not be quite right.

Now for the question: I have been fiddling with node-red and I find that often homekit will send just the “On:true” message without brightness and will show in homekit that the brightness level is whichever brightness was used most recently. Have you noticed this? Have you changed your function since you posted it in March?

I’m thinking I will need to set node-red to remember the last brightness then if there’s a single “On:true” message without a follow up brightness message then just send the most recent brightness through to OpenHAB. Problem is I’m a total noob on the node red thing… Any suggestions would be appreciated!

I’m reading through the node red page on functions and realizing I may have asked a question that you already solved using the first var line.

I should test things before I ask questions. But the question of how things are working and if you’ve changed things since your last post here is still valid.

Thanks!

I’m going to follow up here and first say - thank you for posting your function! It was most helpful. I made a few modifications that the community may be interested in - with this setup I’m unable to get the homekit dimmer out of sync with the openHAB dimmer, so long as I never send “on” or “off” and only pass number states through. While this would be an easy addition to the function, I quite like how it works and can easily avoid sending on/off through my dimmers.

My nodered setup is: openHAB->function1->homekit->function2->rbe->openHAB

Function 1:

var results = msg.payload;
if (results < 1) {
    msg.payload = {
        "On": false
    };
} else {
    msg.payload = {
        "Brightness": results,
        "On": true
    };
}
return msg;

Function 2:

var b = context.get('b')||0;

if(msg.payload.On === false){
    msg.payload = 0;
}
else {
if(msg.payload.Brightness){
    b = msg.payload.Brightness;
        context.set('b',b)
    msg.payload=b;
}
if(msg.payload.On === true){
    msg.payload = b;
}
if(msg.payload.Brightness === 0){
    msg.payload = 0;
}
}
if (msg.hap.context !== undefined )
{
return msg;
}

The only other addition I have is an inject going into the homekit item set to fire once at startup setting brightness to 1 - this seems necessary for homekit to be aware that the light is dimmable.


And a picture of the flow, for good measure in case someone else has been struggling and searching for this solution. I’m amazed more people aren’t discussing this route for homekit in the forums as it opens up so much for homekit users. Once I found this post and got this dimmer function set up I spent my evening trying out everything from sprinkers to button inputs to smoke detectors - I’m hoping to get everything fully switched over soon.

Thanks very much, finally I have run homekit stably.
By Node-RED,

@crxporter Looks good you can share your entire flow. I just do not get it right

sorry for the noob questions

I have put together a more recent tutorial of my whole setup. I don’t have this exact flow anymore but the updated version is on my tutorial.

Note the second post on this tutorial has all of my nodered code.

Feel free to ask more questions!

Link:

1 Like

Cool then I go to the other topic to keep it center

Sorry for not answering, wasn’t really active in this things the past half year as my company took most of my time.
Glad you sorted it out and my post was helpful but your tutorial is much more superior than my little post! Thanks for sharing!

Hey good to see you around again! I took your function and ran with it. You may not have thought much of it but it really was a huge breakthrough for me and became the foundation of my node red setup. Before I found your function here I knew nothing about javascript functions - now I feel like a semi-pro.

Now there’s several other people around doing similar things. Thanks for getting us started!

1 Like

Thanks very much for the kind words.
I have thought quite some time for the function as I have not much codingskills. I can read code but writing isn’t that easy as I do it rarely.
But after figuring out the function I had the feeling it might be a useful snippet and no one else had posted sth. like that so I posted it and hoped someone could make any use of it, so I can hopefully give a tinylittlebit back to this community where I am mostly consuming solutions. And well, here we are someone (you) found it useful so I am very happy now that I was able to contribute a little bit and this contribution was helpful.
I need to read your tutorial completely when I have time… hopyfully soon :slight_smile: