ws2801-connect
Version:
A javascript class to control a WS2801 led stripe
221 lines (202 loc) • 7.27 kB
JavaScript
/*
* Copyright 2017 Armin Junge.
*/
/* global Symbol */
var colorLib = require("color")
/**
* Represents your WS2801 led stripe
*/
class WS2801{
/**
* Creates the representation of the led stripe.
* @param {Object} options - options for the led stripe representation
* @param {Number} options.count - Number of led lights on the stripe (must be greater 0)
* @param {WS2801~spiWrite} options.spiWrite - Callback for writing to the SPI
* @param {Array} options.rgbIndex - [0] = index of red, [1] = index of green, [2] = index of blue;
* in the resulting data array for WS2801; default: [0] = 0, [1] = 1, [2] = 2; red first, green second, blue third
* @returns {WS2801}
*/
constructor(options){
this.count = options.count
this.spiWrite = (typeof options.spiWrite === "function")?options.spiWrite:function(){}
if(Array.isArray(options.rgbIndex) && options.rgbIndex.length === 3){
WS2801.assert(options.rgbIndex.every(item => item >= 0 && item <= 2), "Wrong index supplied")
this.rgbIndex = options.rgbIndex
}
else
this.rgbIndex = [0,1,2]
this.rgbMap = Array(3)
this.rgbIndex.forEach(function(item, index){
this.rgbMap[item] = index
}, this)
}
/**
* Helper method for the assertion of the given expression.
* If the expression is false, the method throws the given message as exception.
* @private
* @param {Boolean} expression - any logical expression
* @param {String} message - text, which will be thrown as exception
* @returns {undefined}
*/
static assert(expression, message){
if(!expression)
throw message
}
/**
* Sets the number of supported led lights.
* @param {Number} number - number of led lights
* @returns {undefined}
*/
set count(number){
WS2801.assert(number > 0, "Only natural numbers are valid")
// Each light has three colors, so we need the tripple of count
this.rgbLights = Array(3 * number)
this.clear()
}
/**
* Returns the number of supported led lights.
* @returns {Number}
*/
get count(){
return this.rgbLights.length / 3
}
/**
* Static helper method to determine red, green and blue values.
* Either from a string like "#fe12a9"
* or from the elements of the array, where the elements are red, green and blue
* or from an array as once element, which is identical to the returning one.
*
* Usually this method will be called using a rest parameter.
* See {@link ws2801#setLight setLight()} or {@link ws2801#fill fill()}
* @example //with String
* let r, g, b
* [r, g, b] = ws2801.rgbFrom(["#fe12a9"])
* // r = 0xfe, g = 0x12, b = 0xa9
* @example //with Elements
* let r, g, b
* [r, g, b] = ws2801.rgbFrom([0xfe, 0x12, 0xa9])
* // r = 0xfe, g = 0x12, b = 0xa9
* @example //with Array
* let r, g, b
* [r, g, b] = ws2801.rgbFrom([[0xfe, 0x12, 0xa9]])
* // r = 0xfe, g = 0x12, b = 0xa9
* @example //with rest parameter
* function rgb(...color) {
* return ws2801.rgbFrom(color)
* }
* rgb("#fe12a9")
* rgb(0xfe, 0x12, 0xa9)
* rgb([0xfe, 0x12, 0xa9])
* @param {Array} color - Having one element with a string or an array
* containing red, green and blue as elements or having three elements
* representing red, green and blue
* @returns {Array} Contains red, green and blue as elements
*/
static rgbFrom(color){
switch(color.length){
case 1:
return (color[0] instanceof Array)?WS2801.rgbFrom(color[0]):colorLib(color[0]).array()
case 3:
return color
default:
WS2801.assert(false, "Wrong number of arguments")
}
}
/**
* Sets the color of the led light specified by the given index number.
* @example //Usage
* leds.setLight(0, "#ff0000") //set first light to red
* leds.setLight(1, 0, 255, 0) //set second light to green
* leds.setLight(2, [0x00, 0x00, 0xff]) //set third light to blue
* @param {Number} number - Index of the led light, starting at 0.
* Must not exceed number of led lights.
* @param {String|...Number|Array} color - Contains a string like "#fe12a9"
* or an array with red, green and blue as elements like [0xfe, 0x12, 0xa9]
* or a parameter list of three elments as red, green and blue like (..., 0xfe, 0x12, 0xa9)
* @returns {WS2801}
*/
setLight(number, ...color)
{
WS2801.assert(number >= 0 && number < this.count, "Given Number is out of range")
let r, g, b
[r, g, b] = WS2801.rgbFrom(color)
// set red
this.rgbLights[3*number+this.rgbIndex[0]] = r & 0xff
// set green
this.rgbLights[3*number+this.rgbIndex[1]] = g & 0xff
// set blue
this.rgbLights[3*number+this.rgbIndex[2]] = b & 0xff
return this
}
/**
* Set possibly all lights with given colors.
* If the number of given colors is to low, the rest goes black.
* Superfluous are ignored.
* @param {Array.<String|Array>} colors
* @returns {WS2801}
*/
setAll(colors){
WS2801.assert(colors instanceof Array, "Parameter must be an array")
this.clear()
colors.forEach(function(item, index){
if(index < this.count)
this.setLight(index, item)
}, this)
return this
}
/**
* Iterator for the supported led lights. Each element is always an array
* with three elements representing red, green and blue.
* @returns {Generator}
*/
*[Symbol.iterator]()
{
for(let n = 0, end = this.count; n < end; ++n)
yield [this.rgbLights[3*n+this.rgbIndex[0]],this.rgbLights[3*n+this.rgbIndex[1]],this.rgbLights[3*n+this.rgbIndex[2]]]
}
/**
* Writes the led lights colors to the led stripe using the supplied SPI instance.
* You have to call this method everytime you want to see the set colors
* on your led stripe.
* @returns {WS2801}
*/
show()
{
this.spiWrite(this.rgbLights)
return this
}
/**
* Sets all supported led lights to black.
* @returns {WS2801}
*/
clear()
{
this.rgbLights.fill(0)
return this
}
/**
* Sets all supported led lights to the given color.
* @example //Usage
* leds.fill("#ff0000") // set all led lights to red
* leds.fill(0, 255, 0) // set all led lights to green
* leds.fill([0x00, 0x00, 0xff]) // set all led lights to blue
* @param {String|...Number|Array} color - Contains a string like "#fe12a9"
* or an array with red, green and blue as elements like [0xfe, 0x12, 0xa9]
* or a parameter list of three elments as red, green and blue like (..., 0xfe, 0x12, 0xa9)
* @returns {WS2801}
*/
fill(...color)
{
let rgb = WS2801.rgbFrom(color)
this.rgbLights.forEach(function(item, index, arr){
arr[index] = rgb[this.rgbMap[index % 3]]
}, this)
return this
}
}
/**
* Callback for writing to the SPI
* @callback WS2801~spiWrite
* @param {Array} data - Data (color information) which has to be transfered to the led stripe
*/
module.exports = WS2801