Twinkly Binding

image

Twinkly lights binding

This binding allows you to control your Twinkly lights.

Readme

As of now you can switch your lamps on / off, adjust brightness and working mode. In movie mode you can also select the current video from the playlist you’ve uploaded to the device.

You can find an extended description including textual configuration on GitHub (link).

Changelog

  • 4.1.0.202311251704
    • OH 4 Release with added Color channel for the color mode
  • 3.2.0.202112051225
    • Hotfix for changing refresh rate
  • 3.2.0.202112041151
    • This is a first release to be published on Marketplace.

Resources

Latest release JAR: https://github.com/Pavion/org.openhab.binding.twinklytree/releases/download/4.1.0.202311251704/org.openhab.binding.twinklytree-4.1.0-SNAPSHOT.jar

For other releases please check the release section on GitHub (link).

Authoring and support

This binding is being developed by @Maarten_v_Hulsentop and @Pavion. It’s a small binding with limited support. If you encounter any issues, please use this thread or create an issue on GitHub (link).

6 Likes

Hi Pavion,

thanks for taking over the development and your time to support this binding in the future.
I was using your initial proof of concept version until today and tried to switch to the latest pre-release 3.2.0.202112041151.

The poc version was throwing “Issue while polling for state” errors and sometimes the command to turn the Twinkly on or off wasn´t sent to the device.
Maybe you could think about a method to check if a command was executed correctly?

The latest pre-release can control the Twinkly but i´m not able to save the refresh rate.
When changing the refresh rate from 0 to e.g. 60, the binding throws an error.

2021-12-05 13:07:18.117 [ERROR] [st.core.internal.thing.ThingResource] - Exception during HTTP PUT request for update config at 'things/twinklytree:twinkly:balkon/config'
java.lang.NullPointerException: null
at org.openhab.binding.twinklytree.internal.TwinklyTreeHandler.dispose(TwinklyTreeHandler.java:301) ~[?:?]
at org.openhab.core.thing.binding.BaseThingHandler.handleConfigurationUpdate(BaseThingHandler.java:105) ~[?:?]
at org.openhab.core.thing.internal.ThingRegistryImpl.updateConfiguration(ThingRegistryImpl.java:94) ~[?:?]
at org.openhab.core.io.rest.core.internal.thing.ThingResource.updateConfiguration(ThingResource.java:498) ~[?:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
at java.lang.reflect.Method.invoke(Method.java:566) ~[?:?]
at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:179) ~[bundleFile:3.4.3]
at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:96) ~[bundleFile:3.4.3]
at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:201) ~[bundleFile:3.4.3]
at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:104) ~[bundleFile:3.4.3]
at org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:59) ~[bundleFile:3.4.3]
at org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:96) ~[bundleFile:3.4.3]
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308) ~[bundleFile:3.4.3]
at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121) ~[bundleFile:3.4.3]
at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:265) ~[bundleFile:3.4.3]
at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:234) ~[bundleFile:3.4.3]
at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:208) ~[bundleFile:3.4.3]
at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:160) ~[bundleFile:3.4.3]
at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:225) ~[bundleFile:3.4.3]
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:298) ~[bundleFile:3.4.3]
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPut(AbstractHTTPServlet.java:234) ~[bundleFile:3.4.3]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:710) ~[bundleFile:3.1.0]
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:273) ~[bundleFile:3.4.3]
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:791) ~[bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:550) ~[bundleFile:9.4.40.v20210413]
at org.ops4j.pax.web.service.jetty.internal.HttpServiceServletHandler.doHandle(HttpServiceServletHandler.java:71) ~[bundleFile:?]
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) ~[bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:602) ~[bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235) ~[bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1624) ~[bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233) ~[bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1435) ~[bundleFile:9.4.40.v20210413]
at org.ops4j.pax.web.service.jetty.internal.HttpServiceContext.doHandle(HttpServiceContext.java:294) ~[bundleFile:?]
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188) ~[bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:501) ~[bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1594) ~[bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186) ~[bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1350) ~[bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) ~[bundleFile:9.4.40.v20210413]
at org.ops4j.pax.web.service.jetty.internal.JettyServerHandlerCollection.handle(JettyServerHandlerCollection.java:82) ~[bundleFile:?]
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.server.Server.handle(Server.java:516) ~[bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:388) ~[bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:633) [bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:380) [bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277) [bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311) [bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105) [bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104) [bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:336) [bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:313) [bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171) [bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:129) [bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:383) [bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:882) [bundleFile:9.4.40.v20210413]
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1036) [bundleFile:9.4.40.v20210413]
at java.lang.Thread.run(Thread.java:829) [?:?]

So i´m currently only able to control Twinkly but without the knowing the current state if there was a change in the app.

kind regards
Michael

Thanks for pointing this out! I’ve made a hotfix.

1 Like

Have an issue getting the binding Online:

As answered on GitHub, when creating the Thing with no refresh rate no active communication with lights is tried so the Thing state stays UNKNOWN. Any successful command (or turning refresh on) would set the Thing to ONLINE.
I agree it’s not quite self-explaining and I might rethink it in the future :slight_smile:

2 Likes

Seems good, I will try it out as I have two different christmas/winter light strips!

Edit:

Only tried switching on and off so far and it seems to work partly only on one strip.

Twinkly Strings Gold Gen2:
Can turn off but not turn on. Need to use the official app to turn it back on.

Twinkly icicle Special edition RGB+W Gen 2:
Can both turn on and off

Could there be a difference in the API here or something?

Oh sorry @Masssssy, hadn’t noticed your edit until now.

Sure there can be differences between used APIs. There seems to be no on mode, so for my lights I have to use movie mode to turn on. Perhaps your lights don’t support it.

Please ensure all your devices are up to date, please turn your refresh on (if not already), link your mode channel and note, which mode is shown when your device is powered on. You can send this mode as String to the channel then to turn your lights on.

Please let me know how it works.

Only tested a little bit but it seems the “Dimmer” works for both turning on and off the light.

Using the switch however does only turn it off, then it has to be started again with the dimmer.

It seems that the one that fully responds runs mode “movie” while the other one runs “effect”. I’m not sure what the difference is to be honest. The official app says effect and both run the same firmware version. Maybe effect = shades of white only for the “gold” edition?

Seems one improvement here could be for the binding to use on/off events for the dimmer. Then the switch could be scrapped altogether. Right now I can only dim using google assistant, not turn on or off.

I assume so… my device seems to ignore the effect mode.
Probably you could create a virtual Switch with a rule to send effect to the mode channel if turned on and off if turned off. You can also use a classic command options button for your sitemap (required options will be autofilled):

Sure, I will play around with it a little bit. I’m not sure if you saw the last thing I added about Google Assistant.

Now if I would add the switch I would only be able to turn on/off. If I add the dimmer I would only be able to dim, not turn on/off. The optimal would be to bind the dimmer to Google Assistant but then the binding would have to be changed to use these on/off events I think.

Because I do not need to care if it’s movie/effect when using the dimmer slider. It simply works on both devices. If that could be sorted out I think the experience would be better :slight_smile: I can see if I have to to have a look as well some day. Time is rare…

If you’re using GA you can use both Switch and Dimmer, please look here for more information:

For your Icicle you should be able to combine both existing Items as described.
For your Gold you can use something like that:

String GoldMode { channel="twinklytree:twinkly:gold:mode" }
Dimmer GoldDimmer { channel="twinklytree:twinkly:gold:brightness", ga="Gold" }
Switch GoldSwitch { ga="Gold" }

with rule:

rule "Gold Switch" 
when 
    Item GoldSwitch received command
then 
    if (receivedCommand == ON) {
        GoldMode.sendCommand("effect")
    } else {
        GoldMode.sendCommand("off")
    } 
end

Before you clear away your Twinkly for the season:

Anybody besides me seeing this?

Sorry, I couldn’t reproduce your issue on my setup. My main server is stable after many restarts, long runtime, plugging and unplugging the lights. On a clean OH 3.2 it starts with offline lights without any issues.
This binding is pretty basic and I’m not aware of any possible conflict with OH core.
Uninstalling the binding should not leave any junk behind.
As I’ve read, you had some other small issues, maybe the impossible stuff combined :slight_smile:
If anyone else encounters the same issue, please enable debug and send me an excerpt.

log:set DEBUG org.openhab.binding.twinklytree

Pav

Strange thing…
What happened was that I cleared away X-mas so the Twinkly became offline.
A few days later, when I was hit be JJS OOM error, I did the first reboot after removing Twinkly.

I tried numerous reboots, power-cycles. Upgraded to latest snapshot, downgraded again, because the snapshot is unstable. Many restart once again.
Always ended up in the same state:

213 │ Active   │  75 │ 3.2.0                 │ openHAB Core :: Bundles :: Thing XML
214 │ Active   │  80 │ 3.2.0                 │ openHAB Core :: Bundles :: Transformation Service
215 │ Active   │  80 │ 3.2.0                 │ openHAB Core :: Bundles :: UI
216 │ Active   │  80 │ 3.2.0                 │ openHAB Core :: Bundles :: UI Icon Support
217 │ Active   │  80 │ 3.2.0                 │ openHAB Core :: Bundles :: Voice
218 │ Active   │  80 │ 3.2.0                 │ openHAB UI :: Bundles :: Main UI
225 │ Active   │  80 │ 1.0.0.201802012106    │ org.osgi:org.osgi.service.jaxrs
226 │ Active   │  80 │ 1.5.0                 │ ThreeTen-Extra
227 │ Active   │  80 │ 1.27.0                │ SnakeYAML
228 │ Active   │  80 │ 2.1                   │ SI Units
229 │ Active   │  80 │ 2.1.0                 │ SI Quantities
230 │ Active   │  80 │ 4.2.1                 │ Stax2 API
231 │ Active   │  80 │ 2.1.2                 │ Units of Measurement Reference Implementation
232 │ Active   │  80 │ 2.1.0                 │ Units of Measurement Common Library for Java
233 │ Active   │  80 │ 1.4.18                │ XStream Core
234 │ Active   │  80 │ 3.2.0.202112051225    │ openHAB Add-ons :: Bundles :: TwinklyTree Binding
235 │ Starting │  80 │ 5.9.0                 │ jna
236 │ Resolved │  80 │ 5.9.0                 │ jna-platform
237 │ Resolved │  80 │ 1.6.2                 │ JavaMail API
238 │ Resolved │  80 │ 1.0.1                 │ IO.Socket Engine Client
239 │ Resolved │  80 │ 1.0.1                 │ IO.Socket Socket Client
240 │ Resolved │  80 │ 20180813.0.0          │ JSON in Java
241 │ Resolved │  80 │ 5.2.1                 │ nrjavaserial
242 │ Resolved │  80 │ 3.7.2                 │ Apache Commons Net
243 │ Resolved │  80 │ 3.8.1.1               │ Apache ServiceMix :: Bundles :: okhttp
244 │ Resolved │  80 │ 1.13.0.1              │ Apache ServiceMix :: Bundles :: okio
245 │ Resolved │  80 │ 2.0.0                 │ Californium (Cf) Core
246 │ Resolved │  80 │ 2.0.0                 │ Californium (Cf) Element Connector
247 │ Resolved │  80 │ 2.0.0                 │ Californium (Cf) OSGi
248

As soon as I disabled the Twinkly addon in the GUI, the system jumped to life (live, no restart, just Twinkly disable), rule engine started, but many addons was still inn the Resolved state.

One thing I wonder: why did the Twinkly addon start so early, long before the other addons?
(Can’t check now since I have uninstalled it)

Since my Twinkly is now in the Attic, it is a topic for the next X-mas season :slight_smile:

I’m still in for the coincidence theory. Bindings stuck at Resolved seems to be a known issue on old setups / upon migration: link, link, link. Perhaps the addons had been resorted upon upgrade so this binding was started before its dependencies. I don’t know… but I do hope I will be there next Christmas and able to provide you any support :slight_smile::christmas_tree:

1 Like

Twinkly supports razor chrome api. I wonder if this could be a back door way at more functionality

https://assets.razerzone.com/dev_portal/REST/html/index.html

Hello to all,

its soon Christmas and I am still a beginner with OH therefore a started earlier to setup the twinkly binding :wink:

I am working with openHAB 3.3.0 - Release Build.

I would confirm that my Twinkly Strings Gen2 RGB+W LED can be turned off but not turned on

Hi,

I have managed it to get a log file.
Is maybe the problem that when I turn on the the twinkly {“mode”:“movie”} is triggered?

2022-11-26 10:13:46.721 [DEBUG] [nklytree.internal.TwinklyTreeHandler] - Handle command OFF with channel twinklytree:twinkly:TwinklyString:switch
2022-11-26 10:13:46.743 [DEBUG] [nklytree.internal.TwinklyTreeHandler] - Request POST http://192.168.178.84/xled/v1/led/mode {"mode":"off"} got response headers {null=[HTTP/1.1 200 OK], Connection=[close], Content-Length=[26], Content-Type=[application/json]} with data {"mode":"off","code":1000} 
2022-11-26 10:13:53.165 [DEBUG] [nklytree.internal.TwinklyTreeHandler] - Handle command REFRESH with channel twinklytree:twinkly:TwinklyString:switch
2022-11-26 10:13:53.183 [DEBUG] [nklytree.internal.TwinklyTreeHandler] - Request GET http://192.168.178.84/xled/v1/led/mode null got response headers {null=[HTTP/1.1 200 OK], Connection=[close], Content-Length=[56], Content-Type=[application/json]} with data {"mode":"off","detect_mode":0,"shop_mode":0,"code":1000} 
2022-11-26 10:13:53.184 [DEBUG] [nklytree.internal.TwinklyTreeHandler] - Handle command REFRESH with channel twinklytree:twinkly:TwinklyString:dimmer
2022-11-26 10:13:53.207 [DEBUG] [nklytree.internal.TwinklyTreeHandler] - Request GET http://192.168.178.84/xled/v1/led/mode null got response headers {null=[HTTP/1.1 200 OK], Connection=[close], Content-Length=[56], Content-Type=[application/json]} with data {"mode":"off","detect_mode":0,"shop_mode":0,"code":1000} 
2022-11-26 10:14:03.211 [DEBUG] [nklytree.internal.TwinklyTreeHandler] - Handle command REFRESH with channel twinklytree:twinkly:TwinklyString:switch
2022-11-26 10:14:03.234 [DEBUG] [nklytree.internal.TwinklyTreeHandler] - Request GET http://192.168.178.84/xled/v1/led/mode null got response headers {null=[HTTP/1.1 200 OK], Connection=[close], Content-Length=[56], Content-Type=[application/json]} with data {"mode":"off","detect_mode":0,"shop_mode":0,"code":1000} 
2022-11-26 10:14:03.236 [DEBUG] [nklytree.internal.TwinklyTreeHandler] - Handle command REFRESH with channel twinklytree:twinkly:TwinklyString:dimmer
2022-11-26 10:14:03.254 [DEBUG] [nklytree.internal.TwinklyTreeHandler] - Request GET http://192.168.178.84/xled/v1/led/mode null got response headers {null=[HTTP/1.1 200 OK], Connection=[close], Content-Length=[56], Content-Type=[application/json]} with data {"mode":"off","detect_mode":0,"shop_mode":0,"code":1000} 
2022-11-26 10:14:09.306 [DEBUG] [nklytree.internal.TwinklyTreeHandler] - Handle command ON with channel twinklytree:twinkly:TwinklyString:switch
2022-11-26 10:14:09.329 [DEBUG] [nklytree.internal.TwinklyTreeHandler] - Request POST http://192.168.178.84/xled/v1/led/mode {"mode":"movie"} got response headers {null=[HTTP/1.1 200 OK], Connection=[close], Content-Length=[26], Content-Type=[application/json]} with data {"mode":"off","code":1104} 

Hi @Koechi
You might need to send another command to your mode Channel instead of using switch.
In your sitemap you can create a new cell with Send Command action for this Channel, which should probably be on or effect

BR Pav

Hi Pav,

thank you for your hint.

I have created a rule

rule "Twinkly Strings On"
when Item TwinklyString_Onoffswitch changed from OFF to ON
    
then
    TwinklyString_Modeselection.sendCommand("effect")  
end

rule "Twinkly Strings Off"
when Item TwinklyString_Onoffswitch changed from ON to OFF
    
then
    TwinklyString_Modeselection.sendCommand("off")  
end

Now it works :slight_smile:

With the sitempap it is possible to turn it off and on too:

        Frame label="Tannenbaum" icon="rgb" {
        Switch item=TwinklyString_Onoffswitch label="Off / On"   
        Selection item=TwinklyString_Modeselection label="Mode Selection" icon="rgb" mappings=[off="Aus",effect="An"]
        Setpoint item=TwinklyString_Brightness label="Brightness [%.1f %%]" minValue=0 maxValue=100 step=1
        Text item=TwinklyString_CurrentEffect label="Current Effect"
        Text item=TwinklyString_CurrentMovie label="Current Movie"
        }

Regards Köchi

1 Like