async createCommand()

in source/services/api/command/lib/command.js [219:351]


  async createCommand(ticket, deviceId, command) {
    const commandModes = ['set-temp', 'set-mode'];
    const powerStatuses = ['HEAT', 'AC', 'OFF'];

    /**
     * The solution is for HVAC devices, and this will suppose the command JSON would contain below keys and values.
     * command {
     *   commandDetails: {
     *     command: "set-temp" | "set-mode",
     *     value: number | "HEAT" | "AC" | "OFF"
     *   },
     *   shadowDetails: {
     *     powerStatus: "HEAT" | "AC" | "OFF",
     *     actualTemperature: number,
     *     targetTemperature: number
     *   }
     * }
     * command.commandDetails are for DynamoDB item, and command.shadowDetails are for sending data to device.
     */
    let isCommandValid = true;
    if (command.commandDetails === undefined
      || command.shadowDetails === undefined
      || commandModes.indexOf(command.commandDetails.command) < 0
      || powerStatuses.indexOf(command.shadowDetails.powerStatus) < 0
      || isNaN(command.shadowDetails.targetTemperature)
      || command.shadowDetails.targetTemperature < 50
      || command.shadowDetails.targetTemperature > 110) {
      isCommandValid = false
    } else {
      if (command.commandDetails.command === 'set-temp') {
        if (isNaN(command.commandDetails.value)) {
          isCommandValid = false;
        } else {
          // Fix temperature precision, only keeps 2 precisions
          let targetTemperature = parseFloat(command.shadowDetails.targetTemperature).toFixed(2);
          if (parseInt(targetTemperature.slice(targetTemperature.indexOf('.') + 1)) === 0) {
            targetTemperature = parseFloat(command.shadowDetails.targetTemperature).toFixed(0);
          }
          command.shadowDetails.targetTemperature = targetTemperature;
          command.commandDetails.value = targetTemperature;
        }
      }
    }

    if (!isCommandValid) {
      return Promise.reject({
        code: 400,
        error: 'InvalidParameter',
        message: 'Body parameters are invalid. Please check the API specification.'
      });
    }

    let docClient = new AWS.DynamoDB.DocumentClient(this.dynamoConfig);
    try {
      let validRegistration = await this._validateUserDeviceRegistration(
        deviceId,
        ticket.sub
      );
      if (validRegistration) {
        let _command = {
          commandId: uuidv4(),
          deviceId: deviceId,
          status: 'pending',
          details: {
            command: command.commandDetails.command,
            value: command.commandDetails.value
          },
          userId: ticket.sub,
          createdAt: moment().utc().format(),
          updatedAt: moment().utc().format(),
        };

        let params = {
          TableName: process.env.COMMANDS_TBL,
          Item: _command,
        };

        await docClient.put(params).promise();

        let shadowDetails = {
          powerStatus: command.shadowDetails.powerStatus,
          actualTemperature: command.shadowDetails.actualTemperature,
          targetTemperature: command.shadowDetails.targetTemperature
        }
        await this.shadowUpdate(_command, shadowDetails); //best practise to update device shadow
        await this.publishCommand(_command, shadowDetails); //publish on IoT topic for the device

        // Sends anonymous metric data
        const anonymousData = process.env.anonymousData;
        const solutionId = process.env.solutionId;
        const solutionUuid = process.env.solutionUuid;

        if (anonymousData === 'true') {
          let metric = {
            Solution: solutionId,
            UUID: solutionUuid,
            Timestamp: moment().utc().format('YYYY-MM-DD HH:mm:ss.S'),
            RemoteCommands: 1,
          };

          let usageMetrics = new UsageMetrics();
          try {
            await usageMetrics.sendAnonymousMetric(metric);
          } catch (e) {
            Logger.error(Logger.levels.INFO, e);
          }
        }

        return Promise.resolve(_command);
      } else {
        Logger.error(
          Logger.levels.INFO,
          `[MissingRegistration] No registration found for device ${deviceId}.`
        );
        return Promise.reject({
          code: 400,
          error: 'MissingRegistration',
          message: `No registration found for device "${deviceId}".`,
        });
      }
    } catch (err) {
      Logger.error(Logger.levels.INFO, err);
      Logger.error(
        Logger.levels.INFO,
        `[CommandCreateFailure] Error occurred while attempting to create command for device ${deviceId}.`
      );
      return Promise.reject({
        code: 500,
        error: 'CommandCreateFailure',
        message: `Error occurred while attempting to create command for device "${deviceId}".`,
      });
    }
  }