Twinkly christmas lights integration

Hi All,
I’m not sure if anyone else has considered this yet. I recently purchased some smart christmas tree lights from ‘Twinkly’, and find them fascinating. ( https://www.twinkly.com/ ) They are wifi enabled, and have built-in control via an Android or Apple app. They’re available at Home Depot, Best Buy, Target, Amazon, etc (depending on which country you’re in).

I imagine you could have a variety of scenarios where incorporating them into Openhab could be nice: A colorful kid-friendly progam until the kids bedtime, then a more sedate program, changing colour when a door/gate is unlocked, changing light colour/program with temperature/humidity/time of day /etc.

It seems there are two potential routes for controlling them:

  1. Via Google Assistant:
    https://www.twinkly.com/total-support/

  2. Via HTTP, through an informal API documentation such as:
    https://github.com/scrool/xled
    https://xled-docs.readthedocs.io/en/latest/protocol_details.html

Has anyone explored this yet? Any suggestions as to which path would be the most efficient?

Cheers, and thanks!

I would say: HTTP Binding with JSON payloads. Search the forum for this setup. There are several examples.

For example: https://xled-docs.readthedocs.io/en/latest/rest_api.html#change-led-operation-mode

You may face a problem with the authentication_token… check how the python command line script handles this.

An alternative would be the Exec Binding calling the python. Actually… I would try this first.

1 Like

Looks like newer firmware might even support MQTT, which should be very easy to integrate with: https://xled-docs.readthedocs.io/en/latest/msqtt_api.html

I haven’t tried it yet, but I’d imagine you could simply set the MQTT broker to your own instead of the mqtt.twinkly.com default.

1 Like

Yep, it is pretty much possible to set your own broker and control the lights. Although control MQTT is rather limited compared to rest API.

Thanks so much to you three - I’m hoping to set some time aside after the 2.4 stable gets released to hopefully chip away at this (there’s some learning needed, so I may start with the MQTT approach and then further refine it afterwards).
Cheers

How did this adventure go?
Thinking about maybe getting a Twinkly or two :slight_smile:

Did you see this?

Hi OMR,
Yes, I had seen that documentation of the private API, which is what convinced me it was a very tangible project.
In the end I never did push through to connect it with Openhab - there were competing tasks, and my kids were quite enamored with the options within the app. My wife isn’t too keen on my idea of making the tree a smart-home display (if enabled, displaying ‘notifications’, temperature (red going to the top, blue coming down, etc), etc). Her response: Can’t a tree be a tree? Well, it can, but it can be more too… Anyway, I may poke around at it this December - if I get the guest room finished, get the heating fixed in my shop, get the garage organized, and manage to not get completely out of shape (keep going to the gym).
If you work on it, I’d be fascinated to see what you come up with.

The Twinkly tree I got last year continues to be a hit this year, by the way - it’s a great product. The gen 2 tree is nicer still, as it allows the music synchronization within the controller itself, not relying on your smartphone’s microphone.

Thanks for the details in this thread!

I’m choosing between the 1st and the 2nd generation. Anyone knows if there are differences in the REST api of those 2 generations?

That’s a great question Tim, and one that I don’t think will be easy to get an answer for. The lights certainly seem to have been improved for Gen 2, and the controller has more memory (allowing for more complicated looping effects - I have run into a memory limitation on one effect so far this year on my Gen 1). I can say that even without integration (I haven’t done the grunt work to try implement it yet), it’s a pretty fun experience, and my kids love designing new effects for it.

The API is an UNOFFICIAL one, that was documented after extensive trial & error. Based on the response below, I don’t imagine that Twinkly would be likely to give any useful response to this question. Both generations appear to be controlled by the same app, if that helps, but I can’t see anywhere if an old version of the app can control the new generation of lights - probably the only clear indication that it would work.

Apparently (according to the XLED docs) Twinkly once had plans for a public API. I emailed them about that about a week ago, and it appears it isn’t an active consideration of theirs. This was the response:

We apologize but at the moment this is not possible.

We have forwarded your proposal to our research and development team, which will certainly take it into account for possible future implementations.

Work is in progress, and day after day, apps and lights will be enriched with many new features, they will always be simpler and more intuitive to use and all this will be possible also thanks to the continuous stimulation of the most careful customers like you; for this reason we ask you to keep us updated and to continue to share feedback and suggestions with us, they are very important!

We remain at your disposal for any further need,

and wish you a good day.

Ticket: [link]

Maura


Twinlky Customer Support

30 Nov at 6:56 PM , [snip]

Hello,

I’m interested in connecting my Twinkly tree (gen 1, 390 LEDs) to my Smart Home (Openhab). I had read that at one time there was talk of releasing a public API for twinkly. Is there one already available? If so, how do I access it? If not, is there one planned and what is the approximate timeline for this?

Thanks

It looks like they were able to get some significant progress over at Home Assistant (the guy who documented the private API is active over there too)
see:


and

edit:
Also useful:

It’s beginning to look a little like christmas :wink: So i’m diving into the Twinkly lights and made a little progress.

What i can achieve in Openhab:

Turning ON the lights
Turning OFF the lights

What i can’t achieve is:

Setting (and reading) the brightness level.

I managed it by using the Python file from : (https://github.com/joshkay/home-assistant-twinkly/blob/master/twinkly.py)

I copied it to a location where i can run scripts from (in my case: /var/lib/openhab2) and called the python file via executecommand (executeCommandLine(“python3 twinkly.py THE_IP_ADDRESS on”). You can change on to off to turn it off :slight_smile:

I’m not a python programmer but i can image we can change the python file so we can send a brightness level or other things to the lights. I tried to change the python file but i only managed to turn the lights off when i send my own brighness command. I use this site as a reference for commands:

Maybe there is someone who knows how to change the python file to send a brightness command.

In the file i added:

BRIGHTNESS_DATA = {MODE: MODE_ENA, TYPE: A, VALUE: BRIGHTNESS}

and called it via: onRequest = urllib.request.Request(url = BRIGHT_URL, headers = HEADERS, data = formatData(BRIGHTNESS_DATA))

But this doesn’t work. Hope someone has a sollution!

1 Like

That’s great - this simple control (on/off) is great to have, and I’ve now implemented it in mys system to have the tree come on with the ‘wake up’ lights now.

I’m also not a python programmer, and if you haven’t already found it, I’m guessing more information on the twinkly API could be found here:https://xled-docs.readthedocs.io/en/latest/
It’s for a different implementation of a twinkly python interface, but it should give clues.
Cheers,
Ben

[edit: I had replied to this post through email, thinking it was a direct message of sorts, thus not realizing this was in the thread where the various resources were discussed at length already - sorry for that!]

YES! I succeeded! I visited a Home assistant forum and there was a guy: David who made a python file for home assistant and he managed to change the brightness. Not sure why, but the syntax is different from the readthedocs.io website.

Long story short, a made a python file which you guys could use. The syntax is simple, just call the file with brightness and it’s value at the end:

python3 twinkly.py 192.168.x.x brightness 50

I use this in openhab in my sitemap and when the livingroom lights dim or go brighter. You can use the excuteCommandLine but you have to whitelist the command (exec.whitelist).

Because I use it this way, the value doesn’t change when i dim the light through the app or by voice command (google). I think it would be possible but then i have to create a timer or make a execute command thing with a timeout. I would have to change the python file so it can read the brightness, but i didn’t try that because it isn’t of any use in my situation.

Only problem is, where to drop the file? Just send me a PM or a location to put it and i will upload it there. I’m on github but I don’t know how to upload files there.

(forgot to press reply earlier this evening haha)

1 Like

Good for you!!

You can drop the code here on the forums - paste it within code fences (the button before the config gear above the message entry text box). The other linked twinkly.py is pretty short, and I imagine the addition of brightness control won’t lengthen it substantially.

Alternately, pastebin or a similar service, and leave the link here.

[for myself, the simple on/off is likely 90% of the benefit. The kids love playing with the app controlling the patterns/movies, so there wouldn’t be meaningful display of house information in the color/pattern/brightness. The last 10% would be a huge undertaking of time, as I’d want to create a fluctuating red blob that changes size with spoken voice - a la HAL from 2001 a Space Odyssey. I think that is one fantasy that is better in my head than seen by my wife after spending far too long isolated in my office working on it. This being said, … nah, I probably shouldn’t…].

1 Like

The code is pretty similar so i just paste it here, one could easily copy it and save it on their openhab setup.

That HAL idea is really awesome but almost impossible i think. Twinkly is very hard to control per LED i read so that’s a no for me. Our christmas tree is next to the TV, that’s the reason i want to dim it when we are watching tv.

So here is the code for anyone who want to use it:

import sys
import json
import urllib.request
import codecs

ARG_ON = 'on'
ARG_OFF = 'off'
ARG_STATE = 'state'
ARG_BRIGHT = 'brightness'

ARG_IP = sys.argv[1]
ARG_ACTION = sys.argv[2]


URL = "http://" + ARG_IP + "/xled/v1/"

LOGIN_URL = URL + "login"
VERIFY_URL = URL + "verify"
MODE_URL = URL + "led/mode"
BRIGHT_URL = URL + "led/out/brightness"

AUTH_HEADER = 'X-Auth-Token'

AUTHENTICATION_TOKEN = 'authentication_token'
CHALLENGE_RESPONSE = 'challenge-response'
MODE = 'mode'
MODE_ON = 'movie'
MODE_OFF = 'off'


HEADERS = {'Content-Type': 'application/json'}
LOGIN_DATA = {'challenge': 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8='}
TURN_ON_DATA = {MODE: MODE_ON}
TURN_OFF_DATA = {MODE: MODE_OFF}

if len(sys.argv) > 3: #check if the thirth argument is given, save it in a variable and create the brightness data variable
    ARG_ACTION2 = int(sys.argv[3])
    BRIGHTNESS_DATA = {'value': ARG_ACTION2, 'type': 'A'}


def formatData(data):
  return json.dumps(data).encode('utf8')

def processRequest(request):
  return urllib.request.urlopen(request)

def processRequestJSON(request):
  loginResponse = processRequest(request)
  reader = codecs.getreader("utf-8")
  return json.load(reader(loginResponse))

# login to api - get challenge response and auth token
loginRequest = urllib.request.Request(url = LOGIN_URL, headers = HEADERS, data = formatData(LOGIN_DATA))
loginData = processRequestJSON(loginRequest)

challengeResponse = loginData[CHALLENGE_RESPONSE]
authToken = loginData[AUTHENTICATION_TOKEN]

HEADERS[AUTH_HEADER] = authToken
verifyData = {CHALLENGE_RESPONSE: challengeResponse}

# verify token by responding with challenge response
verifyRequest = urllib.request.Request(url = VERIFY_URL, headers = HEADERS, data = formatData(verifyData))
verifyData = processRequestJSON(verifyRequest)

def turnOn():
  onRequest = urllib.request.Request(url = MODE_URL, headers = HEADERS, data = formatData(TURN_ON_DATA))
  processRequest(onRequest)
  print(1)

def turnOff():
  offRequest = urllib.request.Request(url = MODE_URL, headers = HEADERS, data = formatData(TURN_OFF_DATA))
  processRequest(offRequest)
  print(0)
  
def setBrightness():
  brightRequest = urllib.request.Request(url = BRIGHT_URL, headers = HEADERS, data = formatData(BRIGHTNESS_DATA))
  processRequest(brightRequest)

def getState():
  modeRequest = urllib.request.Request(url = MODE_URL, headers = HEADERS)
  modeData = processRequestJSON(modeRequest)

  if modeData[MODE] != MODE_OFF:
    print(1)
  else:
    print(0)
    
if ARG_ACTION == ARG_ON:
  turnOn()
elif ARG_ACTION == ARG_OFF:
  turnOff()
elif ARG_ACTION == ARG_STATE:
  getState()
elif ARG_ACTION == ARG_BRIGHT:
  setBrightness()

Use it like this:

python3 twinkly.py 192.168.x.x on
Turn Twinkly on
python3 twinkly.py 192.168.x.x off
Turn Twinkly off
python3 twinkly.py 192.168.x.x brightness 50
Set brightness to 50

2 Likes

Hey Guys,
I’m reading and using a lot files from this database. Perfect and no questions… till now: Could someone create a jar-File? So I could try using my new twinklys via openhab…

Thank you,
Daffy

Hi, this is not a binding or java so far. I just tried the python with my new twinkly - and even if I never worked with python, it worked somehow:
I’ve installed python3 on my NAS via the Qnap-App-Store. Then I copied the script in a .py-file and with running the command from above it switches the Twinkly.
Now I have to find out how to run a .py-file via the webserver and then it should work with the http-binding. There won’t be a state information available - I guess, but switching on/off will be a good first start.

Hm, thank you for your answer…
If I got you correct, there’s still missing one step to connect it to openhab?
I asked the twinkly support team, too. Almost same words like in the upper comment. Here‘s their answer…
“Certainly not this year, I’ll pass your suggestion to our developers team.”

Cause, to be honest… just switching on/off you could solve for the few xmas weeks by using a sonoff S20.

Hi Steffen,
You could use a sonoff/smart plug, but then your on/off times will be further delayed. The device would need to boot up and connect to wifi, then your app would need to find the twinkly dwevice, then you could control it.

It isn’t hard to set up.
Copy the twinkly.py script to a location accessable by openhab. If using Raspberry pi/ openhabian / linux, it is likely /var/lib/openhab2 . Finally, ensure you have python3 installed. You probably do if using openhabian.

Next, figure out the IP address of your twinkly device. I logged into my router to do this. If you can reserve that IP address within your router’s system, this may help ensure the IP address doesn’t change in the future.

Then, go to a rule. I use text rules, and can’t give direction to any of the graphical/new interfaces.
You can certainly get much more complicated (and I did a bit), but in the rule you can add:

executeCommandLine("python3 twinkly.py 192.168.0.101 on")  // turn the christmas tree on

(replacing the 192.168.0.101 with the IP address of your system)

For myself, I created a summy switch called twinklyTree, and then used the following rule to control the device in a much more ‘normal’ manner elsewhere:

default.items (or whatever .items file you wish, located in openhab’s items directory)

...
Switch twinklyTree "Twinkly Christmas Tree" <light>

yourRules.rules (or whatever .rules file you wish, located in openhabs rules directory. Remember to edit the IP address)

rule "Twinkly Christmas Tree Power State"
when 
  Item twinklyTree received command
then
  if (receivedCommand == ON) {
    executeCommandLine("python3 twinkly.py 192.168.0.209 on")  // turn the christmas tree on
  }
  else {
    executeCommandLine("python3 twinkly.py 192.168.0.209 off")  // turn the christmas tree off
  }
end

Finally, I simply toggle the twinklyTree switch in PaperUI, HabPanel, or send a command with twinklyTree.sendCommand(ON) or twinklyTree.sendCommand(OFF).

This should provide a complete and working implementation.

It doesn’t take long at all, you don’t need to buy anything extra (and flash then configure it). Much faster overall. I have several other rules that then turn the tree on or off as needed. I have a simple 433mHz button for the kids to toggle it on / off, and my script to shut everything off at the end of the day turns its lights off. The wake up rule in the morning turns it on.

If you wanted to go above and beyond, you could change the item to be a dimmer instead of a switch, and then send the off command if brightness 0, and otherwise send on and the brightness for all other values - if using Jeroen’s modified twinkly script above.

Edit: updated the rule to use the receivedCommand to reliably use the new command, not the possibly old switch state.

3 Likes

I’m using the Twinkly v1 and tried to implement it into OH.

My approach is not via python, but via the OH rules.
For me the use case was mostly turning it OFF and ON.
Started with the dimmer part, but somehow the commands are not working yet.
I also stopped using the twinkly app, because it invalidates the token used in OH :wink:

If anyone is interested, here are my files:

twinkly.items:

String    TW_Token
Switch    TW_TokenValid

Switch    TW_Power

twinkly.rules:

var String twinklyAddress = "http://ip.of.your.twinkly/xled/v1"
var Timer renewTokenTimer
var String token

 
rule "Token expiration"
when 
    System started or
    Item TW_TokenValid changed to OFF
then

    if (renewTokenTimer !== null) {
        renewTokenTimer.cancel
        renewTokenTimer = null
    }


    logDebug("rules.twinkly", "twinkly address: {}", twinklyAddress)
    val loginResponse = sendHttpPostRequest(twinklyAddress + "/login", "application/json", "{\"challenge\": \"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=\"}")
    logDebug("rules.twinkly", "login response: {}", loginResponse)
    
    token = transform("JSONPATH", "$.authentication_token", loginResponse)
    TW_Token.postUpdate(token)
    val String challengeResponse = transform("JSONPATH", "$.challenge_response", loginResponse)
    val String tokenExpiration = transform("JSONPATH", "$.authentication_token_expires_in", loginResponse)
    val headers = newHashMap("X-Auth-Token" -> token)
    logDebug("rules.twinkly", "headers: {}", headers)
    val verifyResponse = sendHttpPostRequest(twinklyAddress + "/verify", "application/json", "{\"challenge-response\": \"" + challengeResponse + "\"}", headers, 1000)
    logDebug("rules.twinkly", "verify response: {}", verifyResponse)
    
    renewTokenTimer = createTimer(now.plusSeconds(Integer.parseInt(tokenExpiration))) [|
            TW_TokenValid.sendCommand(OFF)
        ] 

    TW_TokenValid.postUpdate(ON)
end


rule "Turn on christmas lights"
when
    Item TW_Power changed to ON
then
    logInfo("rules.twinkly", "Turn christmas lights on")

    if (token === null) {
        TW_TokenValid.sendCommand(OFF)
    }

    val headers = newHashMap("X-Auth-Token" -> token)
    logInfo("rules.twinkly", "headers: {}", headers)
    
    val response = sendHttpPostRequest(twinklyAddress + "/led/mode", "application/json", "{\"mode\": \"movie\"}", headers, 1000)
    logInfo("rules.twinkly", "response: {}", response)
    logInfo("rules.twinkly", "Christmas lights are on")

    val brightnessResponse = sendHttpGetRequest(twinklyAddress + "/led/out/brightness", headers, 1000)  
    logInfo("rules.twinkly", "brightness response: {}", brightnessResponse)
    TW_Brightness.postUpdate(transform("JSONPATH", "$.value", brightnessResponse))  

end
 
rule "Turn off christmas lights"
when
    Item TW_Power changed to OFF
then
    logInfo("rules.twinkly", "Turn christmas lights off")

    if (token === null) {
        TW_TokenValid.sendCommand(OFF)
    }
 
    val headers = newHashMap("X-Auth-Token" -> token)
    val response = sendHttpPostRequest(twinklyAddress + "/led/mode", "application/json", "{\"mode\": \"off\"}", headers, 1000)
    logInfo("rules.twinkly", "response: {}", response)
    logInfo("rules.twinkly", "Christmas lights are off")

    // update brightness value
    TW_Brightness.postUpdate(0)
end

Using the switch TW_Power turns your twinkly on / off

2 Likes