find({timeout = 10, all = false} = {}) {
    if (isValidString(this.device.id) &&
        isValidString(this.device.ip)) {
      // Don't need to do anything
      debug('IP and ID are already both resolved.');
      return Promise.resolve(true);
    }

    // Create new listeners
    const listener = dgram.createSocket({type: 'udp4', reuseAddr: true});
    listener.bind(6666);

    const listenerEncrypted = dgram.createSocket({type: 'udp4', reuseAddr: true});
    listenerEncrypted.bind(6667);

    const broadcastHandler = (resolve, reject) => message => {
      debug('Received UDP message.');

      const parser = new MessageParser({key: UDP_KEY, version: this.device.version});

      let dataRes;
      try {
        dataRes = parser.parse(message)[0];
      } catch (error) {
        debug(error);

        const devParser = new MessageParser({key: this.device.key, version: this.device.version});
        try {
          dataRes = devParser.parse(message)[0];
        } catch (devError) {
          debug(devError);
          reject(error);
          return;
        }
      }

      debug('UDP data:');
      debug(dataRes);

      if (typeof dataRes.payload === 'string') {
        debug('Received string payload. Ignoring.');
        return;
      }

      const thisID = dataRes.payload.gwId;
      const thisIP = dataRes.payload.ip;

      // Try auto determine power data - DP 19 on some 3.1/3.3 devices, DP 5 for some 3.1 devices
      const thisDPS = dataRes.payload.dps;
      if (thisDPS && typeof thisDPS[19] === 'undefined') {
        this._dpRefreshIds = [4, 5, 6];
      } else {
        this._dpRefreshIds = [18, 19, 20];
      }

      // Add to array if it doesn't exist
      if (!this.foundDevices.some(e => (e.id === thisID && e.ip === thisIP))) {
        this.foundDevices.push({id: thisID, ip: thisIP});
      }

      if (!all &&
          (this.device.id === thisID || this.device.ip === thisIP) &&
          dataRes.payload) {
        // Add IP
        this.device.ip = dataRes.payload.ip;

        // Add ID and gwID
        this.device.id = dataRes.payload.gwId;
        this.device.gwID = dataRes.payload.gwId;

        // Change product key if necessary
        this.device.productKey = dataRes.payload.productKey;

        // Change protocol version if necessary
        if (this.device.version !== dataRes.payload.version) {
          this.device.version = dataRes.payload.version;

          // Update the parser
          this.device.parser = new MessageParser({
            key: this.device.key,
            version: this.device.version
          });
        }

        // Cleanup
        listener.close();
        listener.removeAllListeners();
        listenerEncrypted.close();
        listenerEncrypted.removeAllListeners();
        resolve(true);
      }
    };

    debug(`Finding missing IP ${this.device.ip} or ID ${this.device.id}`);

    // Find IP for device
    return pTimeout(new Promise((resolve, reject) => { // Timeout
      listener.on('message', broadcastHandler(resolve, reject));

      listener.on('error', err => {
        reject(err);
      });

      listenerEncrypted.on('message', broadcastHandler(resolve, reject));

      listenerEncrypted.on('error', err => {
        reject(err);
      });
    }), timeout * 1000, () => {
      // Have to do this so we exit cleanly
      listener.close();
      listener.removeAllListeners();
      listenerEncrypted.close();
      listenerEncrypted.removeAllListeners();

      // Return all devices
      if (all) {
        return this.foundDevices;
      }

      // Otherwise throw error
      throw new Error('find() timed out. Is the device powered on and the ID or IP correct?');
    });
  }
Finds an ID or IP, depending on what's missing.
If you didn't pass an ID or IP to the constructor,
you must call this before anything else.
@param {Object} [options] Options object
@param {Boolean} [options.all]
true to return array of all found devices
@param {Number} [options.timeout=10]
how long, in seconds, to wait for device
to be resolved before timeout error is thrown
@example
tuya.find().then(() => console.log('ready!'))
@returns {Promise<Boolean|Array>}
true if ID/IP was found and device is ready to be used