output_devices/RGBLED.js

const Device = require('../devices/Device.js').Device;
const exc = require('../exc.js');
const inherit = require('../tools.js').inherit;
const PWMLED = require ('./PWMLED.js').PWMLED;
const LED = require ('./LED.js').LED;

exports.RGBLED = RGBLED;

/**
 *
 * Represents a full color LED component (composed of red, green, and blue LEDs).
 *
 * Connect the common cathode (longest leg) to a ground pin; connect each of
 * the other legs (representing the red, green, and blue anodes) to any GPIO
 * pins.  You can either use three limiting resistors (one per anode) or a
 * single limiting resistor on the cathode.
 *
 * @param {int} red - The GPIO pin that controls the red component of the RGB LED.
 * @param {int} green - The GPIO pin that controls the green component of the RGB LED.
 * @param {int} blue - The GPIO pin that controls the blue component of the RGB LED.
 * @param {boolean} [active_high] - Set to ``true`` (the default) for common cathode RGB LEDs. If you are
 *      using a common anode RGB LED, set this to ``false``.
 * @param {Array} [initial_value] - The initial color for the RGB LED. Defaults to black ``[0, 0, 0]``.
 * @param {boolean} [pwm] - If ``true`` (the default), construct {@link PWMLED} instances for
 *      each component of the RGBLED. If ``false``, construct regular {@link LED} instances,
 *      which prevents smooth color graduations.
 * @class
 * @augments Device
 * @throws GPIOPinMissing - If one of the pins is not specified.
 */
function RGBLED(red, green, blue, active_high, initial_value, pwm) {
    this._leds = [];
    if (red === undefined || blue === undefined || green === undefined) {
        throw new exc.GPIOPinMissing('red, green, and blue pins must be provided');
    }
    pwm = (pwm === undefined ? true : pwm);
    var LEDClass = pwm ? PWMLED : LED;
    Device.call(this);
    this._leds = [new LEDClass(red, active_high), new LEDClass(green, active_high), new LEDClass(blue, active_high)];
    if (initial_value === undefined) {
        initial_value = [0, 0, 0];
    }
    this.value(initial_value);
}

RGBLED.prototype = inherit(Device.prototype);
RGBLED.prototype.constructor = RGBLED;

/**
 *  Represents the color of the LED as an RGB 3-tuple of ``[red, green, blue]``
 *  where each value is between 0 and 1 if ``pwm`` was ``true`` when the class was constructed
 *  (and only 0 or 1 if not).
 *
 * @param {Array} [value] - If set, the value for each component will be updated.
 * @returns {Array} - If ``value`` is ``undefined`` then returns the current value for each component.
 *
 * @throws OutputDeviceBadValue - If three values are not passed as an array in value.
 * @throws OutputDeviceBadValue - If any of the RGB values are not between 0 and 1.
 * @throws OutputDeviceBadValue - If pwm is false but a value is between 0 and 1.
 */
RGBLED.prototype.value = function(value) {
    if (value === undefined) {
        return [this.red, this.green, this.blue];
    }
    if (value.length < 3) {
        throw new exc.OutputDeviceBadValue('RGB values must be an array of three components');
    }
    var i;
    for (i = 0; i < 3; i++) {
        if (value[i] < 0 || value[i] > 1) {
            throw new exc.OutputDeviceBadValue('each RGB component must be between 0 and 1');
        }
        if (this._leds[i] instanceof LED) {
            if (value[i] !== 0 && value[i] !== 1) {
                throw new exc.OutputDeviceBadValue('each RGB color component must be 0 or 1 with non-PWM RGBLEDs');
            }
        }
    }

    for (i = 0; i < 3; i++) {
        this._leds[i].value(value[i]);
    }
    this.red = this._leds[0].value();
    this.green = this._leds[1].value();
    this.blue = this._leds[2].value();
};

/**
 * Close each pin and release for reuse.
 */
RGBLED.prototype.close = function() {
    var i;
    for (i = 0; i < 3; i++) {
        if (this._leds[i] !== undefined) {
            this._leds[i].close();
            this._leds[i] = undefined;
        }
    }
    this._leds = [];
    Device.prototype.close.call(this);
};

/**
 *
 * @returns {boolean} - If the LED is currently active (not black) then ``true`` otherwise ``false``.
 */
RGBLED.prototype.is_active = function() {
    return (this.value()[0] + this.value()[1] + this.value()[2] > 0);
};

/**
 * Turn the LED on. This equivalent to setting the LED color to white ``[1, 1, 1]``.
 */
RGBLED.prototype.on = function() {
    this.value([1, 1, 1]);
};

/**
 * Turn the LED off. This equivalent to setting the LED color to black ``[0, 0, 0]``.
 */
RGBLED.prototype.off = function() {
    this.value([0, 0, 0]);
};

/**
 * Toggle the state of the device. If the device is currently off (`value` is ``[0, 0, 0[``),
 * this changes it to "fully" on (`value` is ``[1, 1, 1]``).
 * If the device has a specific color, this method inverts the color.
 */
RGBLED.prototype.toggle = function() {
    var current = this.value();
    this.value([1 - current[0], 1 - current[1], 1 - current[2]]);
};

RGBLED.prototype.closed = function() {
    return this._leds.length === 0;
};

/*RGBLED.prototype.blink = function(on_time, off_time, fade_in_time, fade_out_time, on_color, off_color, n, callback) {

 /*
 Make the device turn on and off repeatedly.

 :param float on_time:
 Number of seconds on. Defaults to 1 second.

 :param float off_time:
 Number of seconds off. Defaults to 1 second.

 :param float fade_in_time:
 Number of seconds to spend fading in. Defaults to 0.

 :param float fade_out_time:
 Number of seconds to spend fading out. Defaults to 0.

 :param tuple on_color:
 The color to use when the LED is "on". Defaults to white.

 :param tuple off_color:
 The color to use when the LED is "off". Defaults to black.

 :param int n:
 Number of times to blink; ``None`` (the default) means forever.


 if (this._leds[0] instanceof LED) {
 if (fade_in_time !== undefined) {
 throw new  exc.ValueError('fade_in_time must be 0 with non-PWM RGBLEDs');
 }
 if (fade_out_time !== undefined) {
 throw new exc.ValueError('fade_out_time must be 0 with non-PWM RGBLEDs');
 }
 }

 this._leds[0].blink (on_time, off_time, fade_in_time, fade_out_time, n, callback);
 this._leds[1].blink (on_time, off_time, fade_in_time, fade_out_time, n, callback);
 this._leds[2].blink (on_time, off_time, fade_in_time, fade_out_time, n, callback);

 };

 RGBLED.prototype._stop_blink = function () {
 this._leds[0]._pin._stop_blink();
 this._leds[1]._pin._stop_blink();
 this._leds[2]._pin._stop_blink();
 }

 /* def _blink_device(
 self, on_time, off_time, fade_in_time, fade_out_time, on_color,
 off_color, n, fps=25):
 # Define some simple lambdas to perform linear interpolation between
 # off_color and on_color
 lerp = lambda t, fade_in: tuple(
 (1 - t) * off + t * on
 if fade_in else
 (1 - t) * on + t * off
 for off, on in zip(off_color, on_color)
 )
 sequence = []
 if fade_in_time > 0:
 sequence += [
 (lerp(i * (1 / fps) / fade_in_time, True), 1 / fps)
 for i in range(int(fps * fade_in_time))
 ]
 sequence.append((on_color, on_time))
 if fade_out_time > 0:
 sequence += [
 (lerp(i * (1 / fps) / fade_out_time, False), 1 / fps)
 for i in range(int(fps * fade_out_time))
 ]
 sequence.append((off_color, off_time))
 sequence = (
 cycle(sequence) if n is None else
 chain.from_iterable(repeat(sequence, n))
 )
 for l in self._leds:
 l._controller = self
 for value, delay in sequence:
 for l, v in zip(self._leds, value):
 l._write(v)
 if self._blink_thread.stopping.wait(delay):
 break
 */


/*
 class RGBLED(SourceMixin, Device):


 def blink(
 self, on_time=1, off_time=1, fade_in_time=0, fade_out_time=0,
 on_color=(1, 1, 1), off_color=(0, 0, 0), n=None, background=True):
 """
 Make the device turn on and off repeatedly.

 :param float on_time:
 Number of seconds on. Defaults to 1 second.

 :param float off_time:
 Number of seconds off. Defaults to 1 second.

 :param float fade_in_time:
 Number of seconds to spend fading in. Defaults to 0. Must be 0 if
 ``pwm`` was ``False`` when the class was constructed
 (:exc:`ValueError` will be raised if not).

 :param float fade_out_time:
 Number of seconds to spend fading out. Defaults to 0. Must be 0 if
 ``pwm`` was ``False`` when the class was constructed
 (:exc:`ValueError` will be raised if not).

 :param tuple on_color:
 The color to use when the LED is "on". Defaults to white.

 :param tuple off_color:
 The color to use when the LED is "off". Defaults to black.

 :param int n:
 Number of times to blink; ``None`` (the default) means forever.

 :param bool background:
 If ``True`` (the default), start a background thread to continue
 blinking and return immediately. If ``False``, only return when the
 blink is finished (warning: the default value of *n* will result in
 this method never returning).
 """
 if isinstance(self._leds[0], LED):
 if fade_in_time:
 raise ValueError('fade_in_time must be 0 with non-PWM RGBLEDs')
 if fade_out_time:
 raise ValueError('fade_out_time must be 0 with non-PWM RGBLEDs')

 def pulse(
 self, fade_in_time=1, fade_out_time=1,
 on_color=(1, 1, 1), off_color=(0, 0, 0), n=None, background=True):
 """
 Make the device fade in and out repeatedly.

 :param float fade_in_time:
 Number of seconds to spend fading in. Defaults to 1.

 :param float fade_out_time:
 Number of seconds to spend fading out. Defaults to 1.

 :param tuple on_color:
 The color to use when the LED is "on". Defaults to white.

 :param tuple off_color:
 The color to use when the LED is "off". Defaults to black.

 :param int n:
 Number of times to pulse; ``None`` (the default) means forever.

 :param bool background:
 If ``True`` (the default), start a background thread to continue
 pulsing and return immediately. If ``False``, only return when the
 pulse is finished (warning: the default value of *n* will result in
 this method never returning).
 """
 on_time = off_time = 0
 self.blink(
 on_time, off_time, fade_in_time, fade_out_time,
 on_color, off_color, n, background
 )

 def _stop_blink(self, led=None):
 # If this is called with a single led, we stop all blinking anyway
 if self._blink_thread:
 self._blink_thread.stop()
 self._blink_thread = None




 */