Twinkly Lights Control - Brightness HTTP POST

Hi there, it's that time of year and adventures in Christmas lights are a plenty, I've got some twinkly lights and having taken a bit from this guide I can turn them on and off.

import xled
control = xled.ControlInterface('')
return msg

However I'm banging my head as to how to control the brightness there are instructions here on how to set the brightness but I can't fathom how to lay this out within my flow to assemble the right thing.

Can any one give me some pointers?

POST /xled/v1/led/out/brightness HTTP/1.1
X-Auth-Token: 5jPe+ONhwUY=
Content-Type: application/json
Content-Length: 45
{"mode":"enabled","type": "A","value": "100"}

If I pop a HTTP request node with POST and the IP address of my lights in and inject the list line as json I just get a 404 response.

As ever I'm sure it's something simple. Please help.

Screenshot 2020-12-10 at 21.54.11

Show us how the http request node is configured, and a debug before showing the msg.payload.

Screenshot 2020-12-11 at 10.23.45

Does the attached help?

try the url as

Thanks @E1cid I'm a step closer now as I get the invalid token champions! I guess it's now just a case of building all the necessary info into that payload inject

Based on you screenshot you send your "value" : "50" as string in the Json
what if you send it as integer .. by removing the double quotes around the number

Andy great shout, I think I'm still a step behind that as I need to assemble the message to let me in before I can start giving it direction.

msg.payload = {
return msg;

I've dropped a function node in with the above and changed the HTTP request to the picture below, at least I'm now getting a connection.

I guess I just need to assemble the message correctly now.

In the function you can try to also set the authentication token you received from the Login step with :

msg.headers["X-Auth-Token"] = "5jPe+ONhwUY=";

1 Like

Once you recieve the auth token you will have to send it in the header. for the brightness request.

1 Like

I wanted to do the same and couldn’t find any useful nodes for this so made my own in the end which may fit your needs. Currently I’ve added power, brightness and a few colour templates...

1 Like

Well would you look at that, I am embarrassed I didn't stumble across that in my searching for how to achieve this before.

To try and redeem myself I'll try and crack the HTTP request route also.

the twinkly authentication needs a couple of steps which aren't obvious in the docs. this follows the form...

  • call the login and supply the challenge
  • get authentication_token & challenge_resonse from the reply
  • 1st usage of the token must be to the verify end point, pass challenge_resonse in body (see docs)
  • if all ok you can use the authentication_token

its a bit convoluted really but there are quite a few examples in github., ive linked one of the simpler ones. It does an extra check before login that you dont specifically need though.

also note, if you have an existing token i think they are pretty short lived, so unless you are handling expiry you will probably need to do the login on every call. Hence why i made a node :slight_smile:

Success - Thank you all for the help on this and thanks AndyF for the last the final mile.

Just if anyone is interested this is what I've created, I appreciate it is what @bryzi nodes already do, but here it is all the same.

[{"id":"4167d048.8c39d8","type":"http request","z":"5b9bb1a9.d41be8","name":"","method":"POST","ret":"txt","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","x":470,"y":1520,"wires":[["53729e87.354d78"]]},{"id":"53729e87.354d78","type":"debug","z":"5b9bb1a9.d41be8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":470,"y":1560,"wires":[]},{"id":"b656f4d9.6153b8","type":"function","z":"5b9bb1a9.d41be8","name":"Brightness","func":"//msg.headers = {\"X-Auth-Token\":global.get(\"authtoken\")};\nmsg.payload = {\"mode\":\"enabled\",\"type\": \"A\",\"value\": msg.payload};\nmsg.url= \"\"\nreturn msg;\n\n\n//flow.set(\"authtoken\",msg.payload.authentication_token);\n//flow.set(\"authchalrsp\",msg.payload[\"challenge-response\"]);","outputs":1,"noerr":0,"initialize":"","finalize":"","x":290,"y":1500,"wires":[["ad51bc9f.7f2c8"]]},{"id":"5bb6c869.12c728","type":"http request","z":"5b9bb1a9.d41be8","name":"GetToken","method":"POST","ret":"txt","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","x":240,"y":1380,"wires":[["ec214898.695e28","f369655b.a23338"]]},{"id":"ec214898.695e28","type":"debug","z":"5b9bb1a9.d41be8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":410,"y":1340,"wires":[]},{"id":"5f7b365.bad90c8","type":"function","z":"5b9bb1a9.d41be8","name":"Get token","func":"if (msg.payload === false){return}\nmsg.payload = {\n//msg.headers = {\n'challenge':'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=',\n//'authentication_token':'5jPe+ONhwUY=',\n};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":240,"y":1340,"wires":[["5bb6c869.12c728"]]},{"id":"6f9aeb0d.292d44","type":"inject","z":"5b9bb1a9.d41be8","name":"1 hour","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"3600","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":100,"y":1300,"wires":[["8f241eda.4edda"]]},{"id":"dc7507c5.71fd78","type":"http request","z":"5b9bb1a9.d41be8","name":"Verify","method":"POST","ret":"txt","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","x":670,"y":1380,"wires":[["4b49608e.4b2b2"]]},{"id":"4b49608e.4b2b2","type":"debug","z":"5b9bb1a9.d41be8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":670,"y":1340,"wires":[]},{"id":"3b20a5e0.ea932a","type":"function","z":"5b9bb1a9.d41be8","name":"SetGlobalVar","func":"global.set(\"authtoken\",msg.payload.authentication_token);\nglobal.set(\"authchalrsp\",msg.payload[\"challenge-response\"]);\n\nmsg.headers = {\"X-Auth-Token\":global.get(\"authtoken\")};\nmsg.payload = {\"challenge-response\":global.get(\"authchalrsp\")};\n\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":530,"y":1380,"wires":[["dc7507c5.71fd78"]]},{"id":"f369655b.a23338","type":"json","z":"5b9bb1a9.d41be8","name":"","property":"payload","action":"","pretty":false,"x":390,"y":1380,"wires":[["3b20a5e0.ea932a"]]},{"id":"eda93b11.1254e8","type":"inject","z":"5b9bb1a9.d41be8","name":"10","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"10","payloadType":"num","x":130,"y":1480,"wires":[["b656f4d9.6153b8"]]},{"id":"2beee86e.4f9d18","type":"inject","z":"5b9bb1a9.d41be8","name":"100","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"100","payloadType":"num","x":130,"y":1520,"wires":[["b656f4d9.6153b8"]]},{"id":"8f241eda.4edda","type":"ping","z":"5b9bb1a9.d41be8","mode":"triggered","name":"","host":"","timer":"20","inputs":1,"x":240,"y":1300,"wires":[["5f7b365.bad90c8","84e8124a.5f1dd"]]},{"id":"84e8124a.5f1dd","type":"debug","z":"5b9bb1a9.d41be8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":430,"y":1300,"wires":[]},{"id":"ad51bc9f.7f2c8","type":"function","z":"5b9bb1a9.d41be8","name":"SetHeaders","func":"msg.headers = {\"X-Auth-Token\":global.get(\"authtoken\")};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":470,"y":1480,"wires":[["4167d048.8c39d8"]]}]

Cool, looks great, nice one.

One small tweak id make but you probably wouldn't notice would be to swap SetGlobalVar and Verify around and only set the global if verify was successful. There is a remote possibility that verify could fail or an event could use the token in the tiny window before verify is called. Probably nothing you would see in practice, thats just my software engineering OCD kicking in :slight_smile:


Slightly off-topic. I've installed your node and while the ON and OFF commands work, the twinkly light will not switch on back in the "playlist" mode regardless of the fact that it was the mode it was in when switched off using the OFF command.
If I do the same with an external switch (cut the power to the lights) it will resume correctly when the power comes back on.

P.S. The home-assistant integration suffers from the same issue, the timer in the app however works correctly.

Interesting, I've not had any trouble, my process it to tick the effect and then the sequence resumes. Do you have a single effect or a playlist?


No it is specific to the playlist mode. If I have just an effect selected, then it comes back to it and everything is fine. However it does not return to the playlist. I think it goes back to the first effect in the playlist and stays there.

My workaround is to not switch the lights off, but to switch the brightness to 0 and when I switch them back to 100% the playlist is still going.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.