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)
- recognize that while I have python 3 installed (you need this), I only had pip for python 2.7
sudo apt install python3-pip
- 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.)
- 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)
-
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)
-
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