Executing Commands
You can control the things by sending commands from the mobile applications. The commands are processed on the things to achieve various services. See here for the overview.
Executing commands
To send a command, you will first set the parameters for each action that composes the command. Then, you will execute the method for sending the command to Thing Interaction Framework.
Prior to the execution, you need to get a ThingIFAPI
instance (api
) that is initialized by the steps described in Initializing and Onboarding. You also need the user ID of the user who is going to execute the command.
Here, we will show a sample code block for sending a command based on the example shown in Defining Schema.
// Create an array for actions to be executed as a command.
var actions = [Dictionary<String, AnyObject>]()
// Create actions.
let action1: Dictionary<String, AnyObject> = ["turnPower": ["power": true]]
let action2: Dictionary<String, AnyObject> = ["setPresetTemperature": ["presetTemperature": 25]]
let action3: Dictionary<String, AnyObject> = ["setFanSpeed": ["fanSpeed": 5]]
// Add the actions to the array.
actions.append(action1)
actions.append(action2)
actions.append(action3)
// Execute the command.
api.postNewCommand("AirConditioner-Demo", schemaVersion: 1, actions: actions, completionHandler: { (command: Command?, error: ThingIFError?)-> Void in
if error != nil {
// Handle the error.
return
}
})
This is what is happening in the sample code:
Prepare a set of actions to send as an array of
Dictionary
.In this example, we are setting three actions that we've defined in the schema (
turnPower
,setPresetTemperature
, andsetFanSpeed
) in theactions
.The
actions
is an array of actions. Each action is represented as a pair of the action name and its parameter (Dictionary<String, AnyObject>
). The parameter is to be defined as theAnyObject
; in this example we are setting the parameter asDictionary<String, AnyObject>
. The following figure illustrates the actions defined:You can use any data format for the action parameter. The SDK converts the parameter into a JSON string with the
NSJSONSerialization.dataWithJSONObject
. As long as the parameter can be converted into the format that the thing side expects, it is fine. In the above sample, for example, the thing will receive the action nameturnPower
and its parameter{"power":true}
for theaction1
.The actions created in
action1
toaction3
are stored in an array. The thing will receive them in the same order as you set them here.Note that the above sample is defining the actions separately to simplify the explanation. In your Swift code, you can define them at once like the following:
let actions: [Dictionary<String, AnyObject>] = [["turnPower": ["power": true]],["setPresetTemperature": ["presetTemperature": 25]],["setPresetTemperature": ["presetTemperature": 25]]]
Execute the Command
Call the
postNewCommand
method with the schema name, schema version, and the action array and execute the command. The schema name and version can be used to distinguish the parameter format when the thing parses the JSON string. See Schema in the Function Guide for more details.The command execution is handled asynchronously. When the execution is completed, the
Command
and the error information will be notified by the callback.
The following will happen after executing the command.
The callback will be executed when Thing Interaction Framework receives the command. At this point, the command is not delivered to the thing yet, so the command result in the parameter Command
is undefined. Thing Interaction Framework assigns a command ID for each Command
execution. This ID can later be used to get the command result when the command is executed on the thing and its result is ready.
Thing Interaction Framework will send a request for executing the command to the thing via the MQTT push. The thing will execute the specified three actions accordingly after receiving the request.
Finally, the command result and error codes (if any) are uploaded to Thing Interaction Framework. Thing Interaction Framework will send a push notification to notify the corresponding command ID to the mobile application. The mobile application, using its push notification handler, can get the command result and error codes using the command ID. See Getting Command Result for the details.
Sending a portion of actions
You do not need to use all actions defined in a schema at once. You can send only the required action in a certain situation.
For example, suppose you just want to send "power":false
(power off). Since you do not need to configure the preset temperature and fan speed, you can send a command in the following way:
// Create an action array.
let actions: [Dictionary<String, AnyObject>] = [["turnPower": ["power": false]]]
// Execute the command.
api.postNewCommand("AirConditioner-Demo", schemaVersion: 1, actions: actions, completionHandler: { (command: Command?, error: ThingIFError?)-> Void in
if error != nil {
// Handle the error.
return
}
})
This time, only one action is set in the array. The command sent to the thing, therefore, will have only this action.
Sending with command details
Use the CommandForm
class to register the command title or description when you send a command.
// Create an array for actions to be executed as a command.
var actions = [Dictionary<String, AnyObject>]()
// Create actions.
let action1: Dictionary<String, AnyObject> = ["turnPower": ["power": true]]
let action2: Dictionary<String, AnyObject> = ["setPresetTemperature": ["presetTemperature": 25]]
let action3: Dictionary<String, AnyObject> = ["setFanSpeed": ["fanSpeed": 5]]
// Add the actions to the array.
actions.append(action1)
actions.append(action2)
actions.append(action3)
// Set a title, description, and metadata to be sent with a command.
let title = "Power on"
let description = "Power on and set to 25 deg C"
let metadata: Dictionary<String, AnyObject> = ["iconIndex":3, "issuer":"remoteController"]
// Create a command form from the detailed information.
let commandForm = CommandForm(schemaName: "AirConditioner-Demo", schemaVersion: 1, actions: actions, title: title, commandDescription: description, metadata: metadata)
// Execute the command with the command form.
api.postNewCommand(commandForm, completionHandler: { (command: Command?, error: ThingIFError?)-> Void in
if error != nil {
// Handle the error.
return
}
})
The command to execute is the same as the one in the first snippet but the CommandForm
class uses the title
, the commandDescription
and the metadata
initializers to set command details. You can use these as you like based on the specifications of your application as described in Command Details. See Command Details also for limits such as the maximum length of the fields.
Once the command is executed, you can get the set command details by the title
, the commandDescription
, and the metadata
properties of the Command
class. You can also get the Command
instances by the way described in Getting a List of Registered Commands below in order to get these properties.
Getting command result
When the thing uploads the command result, Thing Interaction Framework will notify the mobile application by sending a push notification via APNs.
The command ID is included in the payload of the push notification. The mobile application can get the entire command with this ID and fetch the command result. By checking the status code and error message in the command result, the mobile application will be able to know the command execution result.
The destination of the push notification with the command ID depends on whether the owner is a pseudo user or a normal user. See Implementation tips for more information.
The following sample code processes the push notification reception handler.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
// Get the command ID from the payload.
let returnedID = userInfo["commandID"] as? String
if returnedID != nil {
// Do something with the returned ID.
}
}
This is what is happening in the sample code:
- The
application:didReceiveRemoteNotification
is the iOS method that will be executed when the device receives the APNs push notification. We will implement a handler for the command results reception here. - Get the ID of the command executed on the thing by getting the
commandID
in the push notification's payloaduserInfo
. If you are using the push notification also for non-IoT SDK purposes,commandID
will be nil when the push notification is not related to the command execution.
If the application is running in the background, the push message will be notified with a different method. Refer to Combinations of Reception Methods and implement your application accordingly.
If the push notification is not properly delivered...
If the push notification does not arrive after the command is being processed on the thing, check the troubleshooting section of the Kii Cloud SDK push notification feature to resolve the issue. You might also want to try the Kii Cloud SDK tutorial to test if the Kii Cloud SDK push notification feature works fine with the simple program.
Note that using the push notification feature with the Thing-IF SDK is a bit different from using it with the Kii Cloud SDK in the following manners:
- When using the Thing-IF SDK, the push notification is always delivered to both development and production environments.
- The API to install a device is different between the Thing-IF SDK and the Kii Cloud SDK. See Installing a device for the details.
- The Thing-IF SDK will wrap some Kii Cloud push notification features (i.e., topic creation, topic subscription, and message sending). These features will be executed automatically by the Thing-IF SDK when you execute the onboarding and when the corresponding event occurs.
Getting a command with the ID
You can get the details of the command using its command ID. By getting the command, you can reference the command result and take appropriate actions in your application.
// Get the command.
api.getCommand(commandID, completionHandler: { (command: Command?, error: ThingIFError?) -> Void in
if error != nil {
// Handle the error.
return
}
// Get each of the action results.
let actionResults: [Dictionary<String, AnyObject>] = command!.actionResults
for actionResult in actionResults {
for (actionName, result) in actionResult {
let succeeded = result["succeeded"] as? Bool
let errorMessage = result["errorMessage"] as? String
}
}
})
Here is what is happening in the sample code:
- Execute the
getCommand
method of theThingIFAPI
instance(api
) with the command ID as an argument to get the entire command from the server. - Extract an array of the action results from the command's
actionResults
. Each element of the array is aDictionary
with one key-value pair. Its key and value contain the action name and result, respectively (Check this figure for the illustrated example). The action name will be the name you've sent with thepostNewCommand
method.
Getting a list of registered commands
You can get a list of all commands that are registered on the server.
If there are many registered commands, you can use the pagination. If there are 30 registered commands, for example, you can get ten commands per page. In this case, you can get all commands by parsing three pages, ten commands at a time.
The following is the sample code.
// Get a list of commands.
api.listCommands(nil, paginationKey: nil, completionHandler: { (commands: [Command]?, paginationKey: String?, error: ThingIFError?) -> Void in
if error != nil {
// Handle the error.
return
}
for command in commands! {
// Do something with each command.
}
// If the next page exists
if (paginationKey != nil) {
// Get the next page of the list.
api.listCommands(nil, paginationKey: paginationKey, completionHandler: { (commands: [Command]?, paginationKey: String?, error: ThingIFError?) -> Void in
if error != nil {
// Handle the error.
return
}
})
}
})
As shown in the sample code, you can get a list of commands with the listCommands
method.
The paginationKey
represents the current page status. When you execute the listCommands
method with a nil value, the first page is returned with the next pagination key being set in the callback's paginationKey
. You can get the next page by setting this value as the pagination key of the next listCommands
execution. When all commands are obtained, a nil value will be returned as the paginationKey
.
The first argument of the listCommands
denotes the number of commands to get (i.e., the size of a page). If you set a nil value, the server will apply the optimal setting. The size of the page set here will be handled in a best-effort manner, so the actual number of commands obtained can be smaller than the specified number. The commands that could not be retrieved can be obtained in the next page.
You can get the commands only in the ascending order. You cannot get them in the descending order.
The list of commands is stored in the commands
of the callback function.