That would be great; at the moment it’s almost impossible to keep straight what’s in the library versus what’s on each PB. With today’s v2 firmware release bringing the language versions up to parity, now I’ve got to export all my patterns off all my 'blazes, convert them to text files and diff them before I’ll know where I need to remove v2 shims and add in arrays and matrices…
To that end, I’ve spent the afternoon knocking together a little applet to export all patterns off all PBs to a folder tree so I can check in a baseline to git; then after I do my language upgrades I can use the commit log to know what I need to re-upload to which PB (sadly, still a manual process).
If anybody wants to do the same, here’s the code:
#!/usr/bin/env python3
import requests, pathlib, json, base64
from pixelblaze import *
from lzstring import LZString
if __name__ == "__main__":
# create a PixelblazeEnumerator object, listen for a couple of seconds, then list the Pixelblazes we found.
pbList = PixelblazeEnumerator()
print("Listening for Pixelblazes...")
time.sleep(3)
# Make a top-level folder to hold the backups.
pathlib.Path('./Backups').mkdir(parents=True, exist_ok=True)
for pixelblazeIP in pbList.getPixelblazeList():
# create a Pixelblaze object.
print("Connecting to Pixelblaze @", pixelblazeIP)
pb = Pixelblaze(pixelblazeIP)
pb.stopSequencer() # so the patterns don't change while we're copying them
hardwareConfig = pb.getHardwareConfig()
pixelblazeName = hardwareConfig['name']
print(" Connected to ", pixelblazeName)
time.sleep(1)
# Make a subfolder for each Pixelblaze.
devicePath = './Backups/' + pixelblazeName
pathlib.Path(devicePath).mkdir(parents=True, exist_ok=True)
# Save the hardware characteristics in case we need to rebuild it at a later date.
hardwareFilename = devicePath + '/deviceSettings.json'
with open(hardwareFilename, 'w') as outfile:
json.dump(hardwareConfig, outfile)
# Make a subfolder for Patterns.
patternPath = devicePath + '/Patterns'
pathlib.Path(patternPath).mkdir(parents=True, exist_ok=True)
# Fetch the patterns and save them..
print(" Fetching patterns:")
for patternID, patternName in pb.getPatternList().items():
# Set the active pattern so we can query it.
print(" Saving pattern '%s'" % patternName)
pb.setActivePattern(patternName)
pb.waitForEmptyQueue(1000)
time.sleep(1)
# save the pattern as a binary blob (.BIN)
patternFilename = patternPath + '/' + patternName.replace("/","_")
suffix = ''
url = 'http://' + pixelblazeIP + '/p/' + patternID + suffix
r = requests.get(url)
binaryData = r.content
open(patternFilename + '.bin', 'wb').write(binaryData)
# also save the pattern as a portable JSON archive (.EPE)
header_size = 36
offsets = struct.unpack('<9I', binaryData[:header_size])
name_offset = offsets[1]
name_length = offsets[2]
jpeg_offset = offsets[3]
jpeg_length = offsets[4]
source_offset = offsets[7]
source_length = offsets[8]
epe = {'name': binaryData[name_offset:name_offset+name_length].decode('UTF-8'), 'id': patternID}
epe['sources'] = json.loads(LZString.decompressFromUint8Array(binaryData[source_offset:source_offset+source_length]))
epe['preview'] = base64.b64encode(binaryData[jpeg_offset:jpeg_offset+jpeg_length]).decode('UTF-8')
with open(patternFilename + '.epe', 'w') as outfile:
outfile.write(json.dumps(epe, indent=2))
# also save the pattern as text (.JS)
with open(patternFilename + '.js', 'w') as outfile:
outfile.write(epe['sources']['main'])
# save the control states, if any.
if len(pb.getControls(patternName)) > 0:
suffix = '.c'
url = 'http://' + pixelblazeIP + '/p/' + patternID + suffix
r = requests.get(url)
open(patternFilename + '.c', 'wb').write(r.content)
time.sleep(1)
pb.close()
print("Complete!")
exit()
You’ll just need to put it in a folder along with a copy of @zranger1’s pixelblaze-client class and @wizard’s lzstring class.