A bit of NYE colour for Holiday!

Here’s a small Python program that uses the SecretAPI to send a stream of randomly coloured lights scrolling down your Holiday.

Usage: python nye.py ip-address-of-holiday optional-frame-rate

And here’s the code.  Copy and paste it in and have fun!

#!/usr/bin/python
#
"""
Holiday class implementation for the Secret API for Holiday by Moorescloud

Homepage and documentation: http://mooresclouddev.markpesce.com/

Copyright (c) 2013, Mark Pesce.
License: MIT (see LICENSE for details)
"""

__author__ = 'Mark Pesce'
__version__ = '1.0b4'
__license__ = 'MIT'

import sys, array, socket

class HolidaySecretAPI:

	NUM_GLOBES = 50

	# Storage for all 50 globe values
	# 
	globes = [ [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00],
	[ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00],
	[ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00],
	[ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00],
	[ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00],
	[ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00],
	[ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00],
	[ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00],
	[ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00],
	[ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00] ]

	def __init__(self, addr=''):
		"""If remote, you better supply a valid address.  
		We'll throw an exception when you don't do this."""
		self.addr = addr    # IP address we're chatting with.
		self.port = 9988	# Secret API port
		self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP

	def setglobe(self, globenum, r, g, b):
		"""Set a globe"""
		if (globenum < 0) or (globenum >= self.NUM_GLOBES):
			return
		self.globes[globenum][0] = r
		self.globes[globenum][1] = g
		self.globes[globenum][2] = b

	def fill(self, r, g, b):
		"""Sets the whole string to a particular colour"""
		for e in self.globes:
			e[0] = int(r)
			e[1] = int(g)
			e[2] = int(b)

	def getglobe(self, globenum):
		"""Return a tuple representing a globe's RGB color value"""
		if (globenum < 0) or (globenum >= self.NUM_GLOBES):
			return False
		return (self.globes[globenum][0], self.globes[globenum][1], self.globes[globenum][2])

	def chase(self, direction="True"):
		"""Rotate all of the globes around - up if TRUE, down if FALSE"""
		return

	def rotate(self, newr, newg, newb, direction="True"):
		if direction == "True":
			for i in range(1, self.NUM_GLOBES):
				self.globes[self.NUM_GLOBES - i][0] = self.globes[self.NUM_GLOBES - (i+1)][0]
				self.globes[self.NUM_GLOBES - i][1] = self.globes[self.NUM_GLOBES - (i+1)][1]
				self.globes[self.NUM_GLOBES - i][2] = self.globes[self.NUM_GLOBES - (i+1)][2]
			self.setglobe(0, newr, newg, newb)
			return
		else:
			return		

		"""Rotate all of the globes up if TRUE, down if FALSE
		   Set the new start of the string to the color values"""
		return

	def render(self):
		"""The render routine sends out a UDP packet using the SecretAPI"""
		# Create the 160-byte array of data
		packet = array.array('B', [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])  # initialize basic packet, ignore first 10 bytes
		for g in self.globes:
			packet.append(g[0])
			packet.append(g[1])
			packet.append(g[2])

		# Send the packet to the Holiday
		self.sock.sendto(packet, (self.addr, self.port))
		return

# Just some basic testerating from the command linery
#
if __name__ == '__main__':
	if len(sys.argv) > 1:
		hol = HolidaySecretAPI(addr=sys.argv[1])
	else:
		sys.exit(1)
	if len(sys.argv) > 2:
		fps = float(sys.argv[2])
	else:
		fps = 10.0
	rate = 1.0/fps
	import random, time
	clr = [0, 1, 2]
	while True:
		clr[0] = random.randint(0, 255)
		clr[1] = random.randint(0, 255)
		clr[2] = random.randint(0, 255)
		clr[random.randint(0,2)] = 0  # knock out one colour component for richness
		hol.rotate(clr[0], clr[1], clr[2])
		hol.render()
		time.sleep(rate)

Update: Had a little think about it, and added some interpolation code. Now the string generates a random colour and moves from that to another random colour in a smooth transition. Essentially it means the globes become a series of rainbow washes moving down the string.

Usage: python nyebow.py ip-of-holiday frames-per-second frames-per-transition (last two are optional and both default to 10)

#!/usr/bin/python
#
"""
Holiday class implementation for the Secret API for Holiday by Moorescloud

Homepage and documentation: http://mooresclouddev.markpesce.com/

Copyright (c) 2013, Mark Pesce.
License: MIT (see LICENSE for details)
"""

__author__ = 'Mark Pesce'
__version__ = '1.0b4'
__license__ = 'MIT'

import sys, array, socket

class HolidaySecretAPI:

	NUM_GLOBES = 50

	# Storage for all 50 globe values
	# 
	globes = [ [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00],
	[ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00],
	[ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00],
	[ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00],
	[ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00],
	[ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00],
	[ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00],
	[ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00],
	[ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00],
	[ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00], [ 0x00, 0x00, 0x00] ]

	def __init__(self, addr=''):
		"""If remote, you better supply a valid address.  
		We'll throw an exception when you don't do this."""
		self.addr = addr    # IP address we're chatting with.
		self.port = 9988	# Secret API port
		self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP

	def setglobe(self, globenum, r, g, b):
		"""Set a globe"""
		if (globenum < 0) or (globenum >= self.NUM_GLOBES):
			return
		self.globes[globenum][0] = r
		self.globes[globenum][1] = g
		self.globes[globenum][2] = b

	def fill(self, r, g, b):
		"""Sets the whole string to a particular colour"""
		for e in self.globes:
			e[0] = int(r)
			e[1] = int(g)
			e[2] = int(b)

	def getglobe(self, globenum):
		"""Return a tuple representing a globe's RGB color value"""
		if (globenum < 0) or (globenum >= self.NUM_GLOBES):
			return False
		return (self.globes[globenum][0], self.globes[globenum][1], self.globes[globenum][2])

	def chase(self, direction="True"):
		"""Rotate all of the globes around - up if TRUE, down if FALSE"""
		return

	def rotate(self, newr, newg, newb, direction="True"):
		if direction == "True":
			for i in range(1, self.NUM_GLOBES):
				self.globes[self.NUM_GLOBES - i][0] = self.globes[self.NUM_GLOBES - (i+1)][0]
				self.globes[self.NUM_GLOBES - i][1] = self.globes[self.NUM_GLOBES - (i+1)][1]
				self.globes[self.NUM_GLOBES - i][2] = self.globes[self.NUM_GLOBES - (i+1)][2]
			self.setglobe(0, newr, newg, newb)
			return
		else:
			return		

		"""Rotate all of the globes up if TRUE, down if FALSE
		   Set the new start of the string to the color values"""
		return

	def render(self):
		"""The render routine sends out a UDP packet using the SecretAPI"""
		# Create the 160-byte array of data
		packet = array.array('B', [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])  # initialize basic packet, ignore first 10 bytes
		for g in self.globes:
			packet.append(g[0])
			packet.append(g[1])
			packet.append(g[2])

		# Send the packet to the Holiday
		self.sock.sendto(packet, (self.addr, self.port))
		return

# Just some basic testerating from the command linery
#
if __name__ == '__main__':
	if len(sys.argv) > 1:
		hol = HolidaySecretAPI(addr=sys.argv[1])
	else:
		sys.exit(1)

	if len(sys.argv) > 2:
		fps = float(sys.argv[2])
	else:
		fps = 10.0
	rate = 1.0/fps

	if len(sys.argv) > 3:
		steps = int(sys.argv[3])
	else:
		steps = 10

	import random, time
	sclr = [ 0, 0, 0 ]
	eclr = [ 0, 0, 0 ]
	diff = [ 0.0, 0.0, 0.0 ]
	clr = [0, 1, 2]
	while True:

		# Copy the end colour to the start colour
		sclr[0] = eclr[0]
		sclr[1] = eclr[1]
		sclr[2] = eclr[2]

		# Generate a random colour end colour
		eclr[0] = random.randint(0, 255)
		eclr[1] = random.randint(0, 255)
		eclr[2] = random.randint(0, 255)
		eclr[random.randint(0,2)] = 0  # knock out one colour component for richness

		# Calculate the difference per frame
		diff[0] = float((eclr[0] - sclr[0]) / steps)
		diff[1] = float((eclr[1] - sclr[1]) / steps)
		diff[2] = float((eclr[2] - sclr[2]) / steps)

		for i in range(steps):
			r = int(sclr[0] + (diff[0] * i))
			if (r < 0): 				r = 0 			elif (r > 255):
				r = 255
			clr[0] = r
			g = int(sclr[1] + (diff[1] * i))
			if (g < 0): 				g = 0 			elif (g > 255):
				g = 255
			clr[1] = g
			b = int(sclr[2] + (diff[2] * i))
			if (b < 0): 				b = 0 			elif (b > 255):
				b = 255
			clr[2] = b

			hol.rotate(clr[0], clr[1], clr[2])
			hol.render()
			time.sleep(rate)

Have fun!

MooresCloud Proudly Sponsors PyConAU

MooresCloud proudly uses Python in its smart Christmas lights

It’s no secret MooresCloud depends heavily on Python and local Python talent, so we’d like to announce our first official corporate sponsorship: as a Conference Contributor for PyConAU 2013. It’s our hope that this contribution will allow other folks with more modest means (students, particularly) can attend the conference and gain the full benefit of all the learning and networking that takes place at such an event.

I was privileged and honoured to be invited to give the opening keynote at the first PyConAU, back in Sydney in 2010. The conference seems to have found a home in Hobart under the steady hand of Chris Neugebauer, and is flourishing.

All of us at MooresCloud are very proud to be able to contribute to the Python community.

From LAMP to LISP

photo - cavorite

photo – cavorite

As our platform has matured (Light and Holiday share a common hardware and software platform), we’ve learned a few things. For example:

  • Apache is overkill on a small profile device like Holiday;
  • Python is wonderful but takes a long time to load – best to do it once;
  • 64 MB of RAM is enough, but not a lot;
  • It’s always better to let Linux do the work.

We touted the Light as the ‘LAMP with a LAMP stack’, which was true – and truly not the best fit for the profile of the platform. In the months of developing since then we’ve come up with some better ways of doing things.   So we have a slightly different stack – just as powerful, with the same logical components, but arranged in a profile more suitable to the resources and requirements of Holiday.

  • Linux – Arch Linux, which we’ve come to know and love;
  • IoTAS – Our Internet of Things Access Server, which provides all HTTP services;
  • SQLite – One of the most useful open source projects of all time;
  • Python – Still our favourite programming language at MooresCloud

Our LAMP stack has become a LISP stack (not to be confused with John McCarthy’s famous LISt Processing language).  Over the next weeks we’ll push IoTAS to our GitHub account, so you can pull it down and see how we’re working to implement the Holiday API. It’s a work-in-progress – as everything is at MooresCloud – but you’ll learn enough to write your own Holiday apps.