output_devices/OutputDevice.js

const GPIODevice = require('../devices/GPIODevice.js').GPIODevice;
const Lock = require('rwlock');
const inherit = require('../tools.js').inherit;

exports.OutputDevice = OutputDevice;
/**
 * Represents a generic GPIO output device.
 * Provides facilities common to GPIO output devices an {@link OutputDevice#on|on} method to switch the device on, a
 * corresponding {@link OutputDevice#off|off} method, and a {@link OutputDevice#toggle|toggle} method.
 *
 * @param {(int | Pin)} pin - The GPIO pin (in BCM numbering) or an instance of Pin that the device is connected to.
 * @param {boolean} [active_high] - If `true` (the default), the {@link OutputDevice#on|on} method will set the GPIO to HIGH.
 * If `false`, the :{@link OutputDevice#on|on} method will set the GPIO to LOW (the {@link OutputDevice#off|off} method always does the opposite).
 * @param {boolean} [initial_value] - If `false` (the default), the device will be off initially.
 * If `undefined`, the device will be left in whatever state the pin is found in when configured for output (warning: this can be on).  If `true`, the device will be switched on initially.
 *
 * @throws GPIOPinMissing - When pin is undefined.
 * @class
 * @augments GPIODevice
 */
function OutputDevice(pin, active_high, initial_value) {
    GPIODevice.call(this, pin);
    this._lock = new Lock();
    this.active_high((active_high === undefined) ? true : active_high);

    if (initial_value === undefined) {
        this._pin.pin_function('output');
    } else {
        this._pin.output_with_state(this._value_to_state(initial_value));
    }
}


OutputDevice.prototype = inherit(GPIODevice.prototype);
OutputDevice.prototype.constructor = OutputDevice;

/**
 * Internal method to apply active state high and convert the actual value to a logical value.
 *
 * @param {boolean|float} value - The value to be converted.
 * @returns {boolean} - The logical value of the pin.
 * @private
 */
OutputDevice.prototype._value_to_state = function(value) {
    return (value) ? this._active_state : this._inactive_state;
};

/**
 * Internal method used to write to the pin after mapping the request value.
 *
 * @param {float | boolean} value - The logical value that the pin should be changed to.
 * @private
 */
OutputDevice.prototype._write = function(value) {
    this._check_open(this);
    this._pin.state(this._value_to_state(value));
};

/**
 * Turns the device on.
 **/
OutputDevice.prototype.on = function() {
    this._pin._stop_blink();
    this._write(true);
};

/**
 * Turns the device off.
 */
OutputDevice.prototype.off = function() {
    this._pin._stop_blink();
    this._write(false);
};

/**
 *
 * This property can be set after construction; be warned that changing it
 * will invert {@link OutputDevice#value|value} (i.e. changing this property doesn't change
 * the device's pin state - it just changes how that state is interpreted).
 *
 * @param {boolean} [value] - When ``true``, the {@link OutputDevice#value|value} property is ``true`` when the device's
 * {@link OutputDevice#pin|pin} is high. When ``false`` the {@link OutputDevice#value|value} property is
 * ``true`` when the device's pin is low (i.e. the value is inverted).
 *
 */
OutputDevice.prototype.active_high = function(value) {
    if (value === undefined) {
        return this._active_state;
    }
    this._active_state = value;
    this._inactive_state = !value;
};

/**
 *
 * @param {boolean | float} [value] - When supplied the device output is changed to the value.
 * @returns {boolean | float} - When value is undefined, the function returns the current value of the device.
 */
OutputDevice.prototype.value = function(value) {
    if (value === undefined) {
        return this._read();
    }
    this._pin._stop_blink();
    this._write(value);
};

/**
 * Reverse the state of the device. If it's on, turn it off; if it's off, turn it on.
 */
OutputDevice.prototype.toggle = function() {
    const that = this;
    this._lock.readLock((release) => {
        if (that.is_active()) {
            that.off();
        } else {
            that.on();
        }
        release();
    });
};