set(options) {
    // Check arguments
    if (options === undefined || Object.entries(options).length === 0) {
      throw new TypeError('No arguments were passed.');
    }

    // Defaults
    let dps;

    if (options.multiple === true) {
      dps = options.data;
    } else if (options.dps === undefined) {
      dps = {
        1: options.set
      };
    } else {
      dps = {
        [options.dps.toString()]: options.set
      };
    }

    options.shouldWaitForResponse = typeof options.shouldWaitForResponse === 'undefined' ? true : options.shouldWaitForResponse;

    // When set has only null values then it is used to get data
    if (!options.isSetCallToGetData) {
      options.isSetCallToGetData = true;
      Object.keys(dps).forEach(key => {
        options.isSetCallToGetData = options.isSetCallToGetData && dps[key] === null;
      });
    }

    // Get time
    const timeStamp = parseInt(Date.now() / 1000, 10);

    // Construct payload
    let payload = {
      t: timeStamp,
      dps
    };

    if (options.cid) {
      payload.cid = options.cid;
    } else {
      payload = {
        devId: options.devId || this.device.id,
        gwId: this.device.gwID,
        uid: '',
        ...payload
      };
    }

    if (this.device.version === '3.4' || this.device.version === '3.5') {
      /*
      {
        "data": {
          "cid": "xxxxxxxxxxxxxxxx",
          "ctype": 0,
          "dps": {
            "1": "manual"
          }
        },
        "protocol": 5,
        "t": 1633243332
      }
      */
      payload = {
        data: {
          ctype: 0,
          ...payload
        },
        protocol: 5,
        t: timeStamp
      };
      delete payload.data.t;
    }

    debug('SET Payload:');
    debug(payload);

    const commandByte = this.device.version === '3.4' || this.device.version === '3.5' ? CommandType.CONTROL_NEW : CommandType.CONTROL;
    const sequenceN = ++this._currentSequenceN;
    // Encode into packet
    const buffer = this.device.parser.encode({
      data: payload,
      encrypted: true, // Set commands must be encrypted
      commandByte,
      sequenceN
    });

    // Make sure we only resolve or reject once
    let resolvedOrRejected = false;

    // Queue this request and limit concurrent set requests to one
    return this._setQueue.add(() => pTimeout(new Promise((resolve, reject) => {
      if (options.shouldWaitForResponse && this._setResolver) {
        throw new Error('A set command is already in progress. Can not issue a second one that also should return a response.');
      }

      // Send request and wait for response
      try {
        if (this.device.version === '3.5') {
          this._currentSequenceN++;
        }

        // Send request
        this._send(buffer).catch(error => {
          if (options.shouldWaitForResponse && !resolvedOrRejected) {
            resolvedOrRejected = true;
            reject(error);
          }
        });
        if (options.shouldWaitForResponse) {
          this._setResolver = data => {
            if (!resolvedOrRejected) {
              resolvedOrRejected = true;
              resolve(data);
            }
          };

          this._setResolveAllowGet = options.isSetCallToGetData;
        } else {
          resolvedOrRejected = true;
          resolve();
        }
      } catch (error) {
        resolvedOrRejected = true;
        reject(error);
      }
    }), this._responseTimeout * 2500, () => {
      // Only gets here on timeout so clear resolver function and emit error
      this._setResolver = undefined;
      this._setResolveAllowGet = undefined;
      delete this._resolvers[sequenceN];
      this._expectRefreshResponseForSequenceN = undefined;

      this.emit(
        'error',
        'Timeout waiting for status response from device id: ' + this.device.id
      );
      if (!resolvedOrRejected) {
        resolvedOrRejected = true;
        throw new Error('Timeout waiting for status response from device id: ' + this.device.id);
      }
    }));
  }
Sets a property on a device.
@param {Object} options Options object
@param {Number} [options.dps=1] DPS index to set
@param {*} [options.set] value to set
@param {String} [options.cid]
if specified, use device id of zigbee gateway and cid of subdevice to set its property
@param {Boolean} [options.multiple=false]
Whether or not multiple properties should be set with options.data
@param {Boolean} [options.isSetCallToGetData=false]
Wether or not the set command is used to get data
@param {Object} [options.data={}] Multiple properties to set at once. See above.
@param {Boolean} [options.shouldWaitForResponse=true] see
[#420](https://github.com/codetheweb/tuyapi/issues/420) and
[#421](https://github.com/codetheweb/tuyapi/pull/421) for details
@example
// set default property
tuya.set({set: true}).then(() => console.log('device was turned on'))
@example
// set custom property
tuya.set({dps: 2, set: false}).then(() => console.log('device was turned off'))
@example
// set multiple properties
tuya.set({
          multiple: true,
          data: {
            '1': true,
            '2': 'white'
         }}).then(() => console.log('device was changed'))
@example
// set custom property for a specific (virtual) deviceId
tuya.set({
          dps: 2,
          set: false,
          devId: '04314116cc50e346566e'
         }).then(() => console.log('device was turned off'))
@returns {Promise<Object>} - returns response from device