Show:
+(function (global, factory) {
  if (typeof exports === 'undefined') {
    factory(global.webduino || {});
  } else {
    module.exports = factory;
  }
}(this, function (scope) {
  'use strict';

  var Module = scope.Module,
    BoardEvent = scope.BoardEvent,
    proto;

  var DHT_MESSAGE = [0x04, 0x04],
    MIN_READ_INTERVAL = 1000,
    MIN_RESPONSE_TIME = 30,
    RETRY_INTERVAL = 6000;

  var DhtEvent = {

    /**
     * Fires when reading value.
     * 
     * @event DhtEvent.READ
     */
    READ: 'read',

    /**
     * Fires when error occured while reading value.
     * 
     * @event DhtEvent.READ_ERROR
     */
    READ_ERROR: 'readError'
  };

  /**
   * The Dht Class.
   *
   * DHT is sensor for measuring temperature and humidity.
   * 
   * @namespace webduino.module
   * @class Dht
   * @constructor
   * @param {webduino.Board} board The board that the DHT is attached to.
   * @param {Integer} pin The pin that the DHT is connected to.
   * @extends webduino.Module
   */
  function Dht(board, pin) {
    Module.call(this);

    this._type = 'DHT11';
    this._board = board;
    this._pin = pin;
    this._humidity = null;
    this._temperature = null;
    this._lastRecv = null;
    this._readTimer = null;
    this._readCallback = function () {};

    this._board.on(BoardEvent.BEFOREDISCONNECT, this.stopRead.bind(this));
    this._messageHandler = onMessage.bind(this);
    this._board.on(BoardEvent.ERROR, this.stopRead.bind(this));
  }

  function onMessage(event) {
    var message = event.message;

    if (message[0] !== DHT_MESSAGE[0] || message[1] !== DHT_MESSAGE[1]) {
      return;
    } else {
      processDhtData(this, message);
    }
  }

  function processDhtData(self, data) {
    var str = '',
      i = 3,
      MAX = 4,
      dd = [],
      d1, d2;

    if (data[2] === self._pin.number) {

      while (i < data.length) {
        d1 = data[i];
        d2 = data[i + 1];
        str += (d1 - 48);
        d2 && (str += (d2 - 48));
        i += 2;

        if ((i - 3) % MAX === 0) {
          dd.push(parseInt(str) / 100);
          str = '';
        }
      }

      self._lastRecv = Date.now();
      self.emit(DhtEvent.READ, dd[0], dd[1]);
    }
  }

  Dht.prototype = proto = Object.create(Module.prototype, {
    constructor: {
      value: Dht
    },

    /**
     * Return the humidity.
     *
     * @attribute humidity
     * @type {Number} humidity
     * @readOnly
     */
    humidity: {
      get: function () {
        return this._humidity;
      }
    },

    /**
     * Return the temperature.
     *
     * @attribute temperature
     * @type {Number} temperature
     * @readOnly
     */
    temperature: {
      get: function () {
        return this._temperature;
      }
    }
  });

  /**
   * Start reading data from sensor.
   *
   * @method read
   * @param {Function} [callback] reading callback.
   * @param {Integer} interval reading interval.
   * @param {Object} callback.data returned data from sensor,
   *                 humidity and temperature will be passed into callback function as parameters.
   *
   *     callback()
   *
   * will be transformed to
   *
   *      callback({humidity: humidity, temperature: temperature})
   *
   * automatically.
   */
  proto.read = function (callback, interval) {
    var self = this,
      timer;

    self.stopRead();

    if (typeof callback === 'function') {
      self._readCallback = function (humidity, temperature) {
        self._humidity = humidity;
        self._temperature = temperature;
        callback({
          humidity: humidity,
          temperature: temperature
        });
      };
      self._board.on(BoardEvent.SYSEX_MESSAGE, self._messageHandler);
      self.on(DhtEvent.READ, self._readCallback);

      timer = function () {
        self._board.sendSysex(DHT_MESSAGE[0], [DHT_MESSAGE[1], self._pin.number]);
        if (interval) {
          interval = Math.max(interval, MIN_READ_INTERVAL);
          if (self._lastRecv === null || Date.now() - self._lastRecv < 5 * interval) {
            self._readTimer = setTimeout(timer, interval);
          } else {
            self.stopRead();
            setTimeout(function () {
              self.read(callback, interval);
            }, RETRY_INTERVAL);
          }
        }
      };

      timer();
    } else {
      return new Promise(function (resolve) {
        self.read(function (data) {
          self._humidity = data.humidity;
          self._temperature = data.temperature;
          setTimeout(function () {
            resolve(data);
          }, MIN_RESPONSE_TIME);
        });
      });
    }
  };

  /**
   * Stop reading value from sensor.
   *
   * @method stopRead
   */
  proto.stopRead = function () {
    this.removeListener(DhtEvent.READ, this._readCallback);
    this._board.removeListener(BoardEvent.SYSEX_MESSAGE, this._messageHandler);
    this._lastRecv = null;

    if (this._readTimer) {
      clearTimeout(this._readTimer);
      delete this._readTimer;
    }
  };

  scope.module.DhtEvent = DhtEvent;
  scope.module.Dht = Dht;
}));