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}".`,
});
}
}