[SOLVED] Control LED-strip through Websocket API?

Hi
I realize I’m swimming at the deep end here, but here’s what I want to do.

I have a Pixelblaze LED controller with a SK6812 strip. It has a Websocket API where it e.g. can use JSON to select pattern, change brightness etc. More info about API here

Is it possible to integrate this into Openhab in some way? Mainly I would like to be able to turn on/off, set brightness and select patterns.

Any hint would be appreciated. Thanks.

Yes


or

Thanks! But, are you sure? I did review the http binding, but it seems it does not have any Websocket support - only http. Maybe I have misunderstood something. If it does, do you know where I can read more about how to use it with Websocket? I could only find info about http usage.

Sorry, websocket…
See this: http://noderedguide.com/node-red-lecture-3-example-3-7-using-websockets-with-node-red/
Node red integration with node-red is very easy

Or use websockets with HABApp if you want to do it in python.

1 Like

Thanks! I haven’t worked with Node red before, but WOW! I’m impressed - that’s a really powerful tool! After install and some Googleling, I got everything set up and working in no time!!

You are welcome, please share your solution, thanks

I agree, @zugarellli , could you please share your solution?
I’m now looking to do a very similar thing - having OpenHab control my new pixelblaze v3
Thanks

So, as opposed to running down the node red path, I opted to use python. This is on a RPi4 (openhabian) system, so slight modifications for other systems may be needed.

reference: GitHub - zranger1/pixelblaze-client: Python 3 module for controlling Pixelblaze
This library seems quite fully featured, and can have any extent of customization

Steps (re-ordered)

  1. recognize that while I have python 3 installed (you need this), I only had pip for python 2.7
sudo apt install python3-pip
  1. install the python websocket client
pip3 install websocket-client

(not sure, you may possibly need to run the above line as openhab, so:
sudo -u openhab pip3 install websocket-client
It appears python dependencies may be installed in a user-specific mode, so what you install with pip3 for openhabian (or your other usual user) won’t necessarily be available to openhab otherwise.)

  1. copy pixelblaze.py library into an accessible directory for your openhab installation
    pixelblaze-client/pixelblaze.py at main · zranger1/pixelblaze-client · GitHub

I used the user data directory (/var/lib/openhab2)

  1. make a python script to take command line arguments and pass them on to the python pixelblaze library. (See my quick and dirty one at the bottom)

  2. Add calls to your script in your rules.
    ie:

executeCommandLine("python3 PBScript.py 192.168.1.234 on")  // turn the lights on

Finally, for reference:
My quick and dirty script, largely a proof of concept, created with reference to example.py from JEM (ZRanger1). All the print statements can likely be removed once you have everything working correctly from the command line. ListPB is only useful from the command line, largely if you don’t know the IP addresses of the pixelblazes. (It is highly valuable to set the PB devices to a static IP address on your system).
The fade in and fade out functions are far too coarse - possibly making this take 100 steps rather than 10 would be a good idea, or possibly finessing them to take x steps over a variable period of time.
Finally, it seems a standard is setting the pattern to an ‘off’ pattern is a good idea - I imagine it decreases the computing occurring on the chip, rather than just suppressing the output. I have yet to do this.
Also, I really haven’t sorted through what the purpose of the ‘pb.waitForEmptyQueue(1000)’ call. It’s likely in the wrong place and used incorrectly. From the pixelblaze.py file, the documentation states it waits until the websocket message queue is empty or until the timeout in milliseconds has elapsed. It returns true if an empty queue acknowledgement was received, or false if the timeout occurs. Given that my code only does one thing at a time (for the most part), I largely ignored this while throwing together this proof of concept last night.

#!/usr/bin/env python3
"""
 uses the pixelblaze library from, and based partially on the example scripy by JEM(ZRanger1)
 Requires the pixelblaze, which also requires websocket-client
Note, the functions for Oasis, sound, and sunrise depend on specific patterns being installed on your pixelblaze.  Adapt to patterns specific to your installation.
"""
from pixelblaze import *
import sys

def listPB():
    pbList = PixelblazeEnumerator()
    print("Testing: PixelblazeEnumerator object created -- listening for Pixelblazes")
    time.sleep(2)
    print("Available Pixelblazes: ", pbList.getPixelblazeList())

def setOasis():
    pb.stopSequencer()              # make sure the sequencer isn't running
    pb.setActivePattern('Oasis')
    pb.waitForEmptyQueue(1000)
    pb.setControl('aura', 0.66667, False)  # ensure the colors are the default blues, as they are modified in some of my other scripts)
    pb.setBrightness(100)  # as often the lights will have been turned off, and Oasis is the default pattern for smart home toggling

def setSound():
    pb.stopSequencer()              # make sure the sequencer isn't running   
    pb.setActivePattern('sound - pew-pew-pew!')
    pb.setBrightness(100)

def setSunrise():
    pb.stopSequencer()              # make sure the sequencer isn't running
    pb.setActivePattern('Sunrise')
    pb.setBrightness(100)

def setBrightness(brightness):
    pb.setBrightness(brightness)


def ListPatterns():
    result = pb.getPatternList()
    for key, value in result.items():
        print(key, ' : ', value)
    time.sleep(1)

def ListVariables():   
    print("Variables: ",pb.getVars())
    pb.close()

def setColor(color):
    pb.setControl('aura', color, False)


def transitionBrightness(pb,level,seconds):
    """
    Changes brightness on the Pixelblaze object <pb> to <level> over
    <seconds> seconds. Note that this call will block execution for
    the entire interval of the transition.  It's a great candidate
    for use w/the async library, (or for threading if you're really
    old school.)
    """
    # figure out how many requests we'll be sending and how quickly
    maxRequestsPerSec = 5
    waitInterval = 1 / maxRequestsPerSec;    
    steps = maxRequestsPerSec * seconds;
    
    # get the current brightness
    result = pb.getHardwareConfig()
    initialBrightness = result['brightness']
    
    # If levels are equal we're done.  Take early out for
    # performance
    if (initialBrightness == level) :
        return
       
    # Do the fade out if we're going to a lower level
    if (initialBrightness > level) : 
        i = steps
        linearDelta = (initialBrightness - level) / steps           
        while (i > 0) :
            newBrightness = level + ((linearDelta * i)) * pow(i/steps,3)    
            pb.setBrightness(newBrightness)
            time.sleep(waitInterval)
            i = i - 1
    # Otherwise fade (new brightness is higher)         
    else :                   
        i = 1
        totalwait = 0;
        linearDelta = (level - initialBrightness) / steps        
        while (i <= steps) :
            newBrightness = initialBrightness + (((linearDelta * i)) * pow(i/steps,3))
            pb.setBrightness(newBrightness)
            time.sleep(waitInterval)
            i = i + 1

	
ARG_IP = sys.argv[1]
ARG_ACTION = sys.argv[2]
pb = Pixelblaze(ARG_IP)

if ARG_ACTION == 'fadein':
    transitionBrightness(pb,100,3)
elif ARG_ACTION == 'fadeout':
    transitionBrightness(pb,0,3)
elif ARG_ACTION == 'on':
    setBrightness(1)
elif ARG_ACTION == 'off':
    setBrightness(0)
elif ARG_ACTION == 'list':
    listPB()
elif ARG_ACTION == 'brightness':
    newBrightness = int(sys.argv[3])
    setBrightness(newBrightness)
elif ARG_ACTION == 'transition':
    newBrightness = int(sys.argv[3])
    transitionBrightness(pb,newBrightness,3)
elif ARG_ACTION == 'oasis':
    setOasis()
elif ARG_ACTION == 'sunrise':
    setSunrise()
elif ARG_ACTION == 'sound':
    setSound()
elif ARG_ACTION == 'pattern':
    pb = Pixelblaze(ARG_IP)
    pb.stopSequencer()              # make sure the sequencer isn't running
    print("Attempting to set pattern to: ", sys.argv[3])
    pb.setActivePattern(sys.argv[3])
    pb.close()
elif ARG_ACTION == 'listvariables':
    ListVariables()
elif ARG_ACTION == 'listpatterns':
    ListPatterns()
elif ARG_ACTION == 'aura':
    setColor(float(sys.argv[3]))
else:
    print("Unrecognized Command")

pb.close()

And my rule, triggered by a 433 MHZ button through a sonoff RF-bridge, in RF433.rules

    case "1DB482" : {
	  logInfo("RF433.rules", "Received data from Double 1 Right.  Sending signal for turning Pixelblaze on")
      executeCommandLine("python3 PBScript.py 192.168.1.234 on")  // turn the lights on
    }	

(note that you will need to change my made-up IP address of 192.168.1.234 to whatever the correct address of your PB device is)

edit: fixed example script to have more finessed fade, and fixed command line parsing of brightness variable (converting to integer first)

edit: fade in and fade out are giving me grief, with it often throwing a timeout error that isn’t being caught…

edit: fixed pip3 commands.
edit: updated python script with many bugfixes, with help from ZRanger1 on the pixelblaze forum

Hi Ben
I’m glad you got it working.

The Python option is way too advanced for me. Using the graphical UI in Node Red was actually a quite simple solution. But, of course Node Red will have to be installed and running, so if host resources are scarce, maybe this is not a viable option.

Node Red works with flows, so for each required operation, a flow must be created. In Node Red, there’s a plug-in for OpenHAB that can either read or set value to OH items. The same way as for OH, there’s a function in Node Red to connect through Websocket. Through the Websocket connection you can convert the value to or from a JSON string that is used by the Pixelblaze. Basically one flow for read and another one for write.

The functionality is somewhat limited, but the Pixelblaze can provide a JSON string for patterns (when they change) or a bit more detailed info, pattern, brightness etc if a status request is sent.

So, working with patterns is quite easy. You provide a JSON string value for the pattern to the PB when you want to change it. When a pattern is changed, the PB is providing a JSON string back with the active pattern. Works both ways.

You can do the same for brightness (turning lights on/dim/off). Unfortunately, the PB will not automatically reply back with the currently set brightness level (e.g. you have a manual switch to turn on/off the lights). So, brightness level is unfortunately one way only using the power of the Websocket.

There is however a status command you can send to the PB that will reply back with a handful settings, brightness level included. So, you can always poll the PB to check, but brightness level will not be provided to you automatically as pattern changes are.

Example flow reading the pattern and update the OH item with current value.

Same goes the other way, setting the pattern from OH

Note: My PB is currently disconnected as you can see in the flow.

Interesting.
I might have to dip my toes into node red some time.