UNPKG

johnny-five-electron

Version:

Temporary fork to support Electron (to be deprecated)

309 lines (260 loc) 7.18 kB
var Board = require("../lib/board"); var EVS = require("../lib/evshield"); var Pins = Board.Pins; var __ = require("../lib/fn"); var events = require("events"); var util = require("util"); // Button instance private data var priv = new Map(); var aliases = { down: ["down", "press", "tap", "impact", "hit"], up: ["up", "release"] }; var Controllers = { DEFAULT: { initialize: { value: function(opts, dataHandler) { var state = priv.get(this); if (Pins.isFirmata(this) && typeof opts.pinValue === "string" && opts.pinValue[0] === "A") { opts.pinValue = this.io.analogPins[+opts.pinValue.slice(1)]; } this.pin = Number.isNaN(+opts.pinValue) ? opts.pinValue : +opts.pinValue; this.io.pinMode(this.pin, this.io.MODES.INPUT); // Enable the pullup resistor after setting pin mode if (this.pullup) { this.io.digitalWrite(this.pin, this.io.HIGH); } this.io.digitalRead(this.pin, function(data) { if (data !== state.last) { dataHandler(data); } }); } }, toBoolean: { value: function(raw) { return raw === this.downValue; } } }, EVS_EV3: { initialize: { value: function(opts, dataHandler) { var state = priv.get(this); state.previous = 0; state.shield = EVS.shieldPort(opts.pin); state.register = EVS.Touch; state.ev3 = new EVS(Object.assign(opts, { io: this.io })); state.ev3.setup(state.shield, EVS.Type_EV3_TOUCH); state.ev3.read(state.shield, EVS.Touch, EVS.Touch_Bytes, function(data) { var value = data[0]; // Since i2cRead is continuous regardless of the reading, // and digitalRead is continuous but only called for changes // in reading value, we need to suppress repeated calls to // dataHandler by limiting to only changed values. if (state.previous !== value) { dataHandler(value); } state.previous = value; }); } }, toBoolean: { value: function(raw) { return raw === this.downValue; } } }, EVS_NXT: { initialize: { value: function(opts, dataHandler) { var state = priv.get(this); state.previous = 0; state.shield = EVS.shieldPort(opts.pin); state.ev3 = new EVS(Object.assign(opts, { io: this.io })); state.ev3.setup(state.shield, EVS.Type_ANALOG); state.ev3.read(state.shield, state.shield.analog, EVS.Analog_Bytes, function(data) { var value = data[0] | (data[1] << 8); // Since i2cRead is continuous regardless of the reading, // and digitalRead is continuous but only called for changes // in reading value, we need to suppress repeated calls to // dataHandler by limiting to only changed values. value = value < 300 ? 1 : 0; if (state.previous !== value) { dataHandler(value); } state.previous = value; }); } }, toBoolean: { value: function(raw) { return raw === this.downValue; } } } }; /** * Button * @constructor * * five.Button(); * * five.Button({ * pin: 10 * }); * * * @param {Object} opts [description] * */ function Button(opts) { if (!(this instanceof Button)) { return new Button(opts); } var pinValue; var raw; var invert = false; var downValue = 1; var upValue = 0; var controller = null; var state = { interval: null, last: null }; // Create a 5 ms debounce boundary on event triggers // this avoids button events firing on // press noise and false positives var trigger = __.debounce(function(key) { aliases[key].forEach(function(type) { this.emit(type, null); }, this); }, 7); pinValue = typeof opts === "object" ? opts.pin : opts; Board.Component.call( this, opts = Board.Options(opts) ); opts.pinValue = pinValue; if (opts.controller && typeof opts.controller === "string") { controller = Controllers[opts.controller.toUpperCase()]; } else { controller = opts.controller; } if (controller == null) { controller = Controllers.DEFAULT; } Object.defineProperties(this, controller); // `holdtime` is used by an interval to determine // if the button has been released within a specified // time frame, in milliseconds. this.holdtime = opts.holdtime || 500; // `opts.isPullup` is included as part of an effort to // phase out "isFoo" options properties this.pullup = opts.pullup || opts.isPullup || false; // Turns out some button circuits will send // 0 for up and 1 for down, and some the inverse, // so we can invert our function with this option. // Default to invert in pullup mode, but use opts.invert // if explicitly defined (even if false) invert = typeof opts.invert !== "undefined" ? opts.invert : (this.pullup || false); if (invert) { downValue = downValue ^ 1; upValue = upValue ^ 1; } state.last = upValue; // Create a "state" entry for privately // storing the state of the button priv.set(this, state); Object.defineProperties(this, { value: { get: function() { return Number(this.isDown); } }, invert: { get: function() { return invert; }, set: function(value) { invert = value; downValue = invert ? 0 : 1; upValue = invert ? 1 : 0; state.last = upValue; } }, downValue: { get: function() { return downValue; }, set: function(value) { downValue = value; upValue = value ^ 1; invert = value ? true : false; state.last = upValue; } }, upValue: { get: function() { return upValue; }, set: function(value) { upValue = value; downValue = value ^ 1; invert = value ? true : false; state.last = downValue; } }, isDown: { get: function() { return this.toBoolean(raw); } } }); if (typeof this.initialize === "function") { this.initialize(opts, function(data) { var err = null; // Update the raw data value, which // is used by isDown = toBoolean() raw = data; if (!this.isDown) { if (state.interval) { clearInterval(state.interval); } trigger.call(this, "up"); } if (this.isDown) { trigger.call(this, "down"); state.interval = setInterval(function() { if (this.isDown) { this.emit("hold", err); } }.bind(this), this.holdtime); } state.last = data; }.bind(this)); } } util.inherits(Button, events.EventEmitter); /** * Fired when the button is pressed down * * @event * @name down * @memberOf Button */ /** * Fired when the button is held * * @event * @name hold * @memberOf Button */ /** * Fired when the button is released * * @event * @name up * @memberOf Button */ module.exports = Button;