diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index 51a8c9d41..fffbbafef 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -1,3 +1,4 @@ +Add: use mqtt.qos and mqtt.retain values from command for command execution (#504) Add: log in info level command and configuration MQTT FIX: check ngsi version in configuration handler (#500) Add missed global config env vars (IOTA_CONFIG_RETRIEVAL, IOTA_DEFAULT_KEY, IOTA_DEFAULT_TRANSPORT) diff --git a/docs/usermanual.md b/docs/usermanual.md index f12807abf..ec973e7e4 100644 --- a/docs/usermanual.md +++ b/docs/usermanual.md @@ -85,6 +85,55 @@ following query parameters: - **k (API Key)**: API Key for the service the device is registered on. - **t (timestamp)**: Timestamp of the measure. Will override the automatic IoTAgent timestamp (optional). + +======= +#### Sending Commands + +MQTT devices commands are always push. For HTTP Devices commands to be push they **must** be provisioned with the +`endpoint` attribute, that will contain the URL where the IoT Agent will send the received commands. Otherwise the +command will be poll. When using the HTTP transport, the command handling have two flavours: + +- **Push commands**: The request payload format will be a plain JSON, as described in the "Payload" section. The + device will reply with a 200OK response containing the result of the command in the JSON result format. + +- **Polling commands**: in this case, the Agent does not send any messages to the device, being the later responsible + of retrieving them from the IoTAgent whenever the device is ready to get commands. In order to retrieve commands + from the IoT Agent, the device will send the query parameter 'getCmd' with value '1' as part of a normal measure. As + a result of this action, the IoTAgent, instead of returning an empty body (the typical response to a measurement + report), will return a list of all the commands available for the device, in JSON format: each attribute will + represent a command, and its value the command value. The use of a JSON return object implies that only one value + can be returned for each command (last value will be returned for each one). Implementation imposes another + limitation in the available values for the commands: a command value can't be an empty string, or a string composed + exclusively by whitespaces. Whenever the device has completed the execution of the command, it will send the + response in the same way measurements are reported, but using the **command result format** as exposed in the + [Protocol section](#protocol). + +Some additional remarks regarding polling commands: + +- Commands can be also retrieved without needed of sending a mesaure. In other words, the device is not forced to send + a measure in order to get the accumulated commands. However, in this case note that `GET` method is used to carry + the `getCmd=1` query parameter (as they are no actual payload for measures, `POST` wouldn't make too much sense). +- MQTT devices can configure (at provisioning and updating time) each command with different values of MQTT QoS and MQTT retain values, which will be used only by a command. Moreover, in the same MQTT device different commands can be configured to use different MQTT options related with QoS level and Retain message policy. I.E: + +```json +{ + + "commands": [ + { + "type": "command", + "name": "a_command_name_A", + "mqtt": { "qos": 2, "retain": true } + }, + { + "type": "command", + "name": "a_command_name_B", + "mqtt": { "qos": 1, "retain": false } + } + ] + +} +``` + #### Configuration retrieval The protocol offers a mechanism for the devices to retrieve its configuration (or any other value it needs from those diff --git a/lib/bindings/MQTTBinding.js b/lib/bindings/MQTTBinding.js index 316613cc4..7db5da2a1 100644 --- a/lib/bindings/MQTTBinding.js +++ b/lib/bindings/MQTTBinding.js @@ -329,16 +329,37 @@ function stop(callback) { */ function executeCommand(apiKey, device, serializedPayload, callback) { const options = {}; - if (config.getConfig().mqtt.qos) { - options.qos = parseInt(config.getConfig().mqtt.qos) || 0; - } - if (config.getConfig().mqtt.retain === true) { - options.retain = config.getConfig().mqtt.retain; - } - config.getLogger().debug(context, 'Sending cmd to the device:\n %j', serializedPayload); + // retrieve command mqtt options from device + var cmdName = Object.keys(JSON.parse(serializedPayload))[0]; + var commands = Object.assign({}, ...device.commands.map((c) => ({[c.name]: c}))); + + options.qos = + commands[cmdName].mqtt && commands[cmdName].mqtt.qos + ? commands[cmdName].mqtt.qos + : config.getConfig().mqtt.qos + ? parseInt(config.getConfig().mqtt.qos) + : 0; + options.retain = + commands[cmdName].mqtt && commands[cmdName].mqtt.retain + ? commands[cmdName].mqtt.retain + : config.getConfig().mqtt.retain + ? config.getConfig().mqtt.retain + : false; + const commandTopic = '/' + apiKey + '/' + device.id + '/cmd'; + config.getLogger().debug( + context, + 'Sending command execution to [%s] with payload [%s] and with mqtt options [%j]', + commandTopic, + serializedPayload, + options); mqttClient.publish(commandTopic, serializedPayload, options); - config.getLogger().info(context, 'Cmd:\n %j was sent to the device %s', serializedPayload, commandTopic); + config.getLogger().info( + context, + 'Cmd:\n %j was sent to the device %s with mqtt options %j', + serializedPayload, + commandTopic, + options); callback(); }