Skip to content

Commit fba8e72

Browse files
munkhuushmglgguuss
authored andcommitted
Merge cmd features (GoogleCloudPlatform#1275)
* Merge command features
1 parent f454910 commit fba8e72

File tree

6 files changed

+150
-11
lines changed

6 files changed

+150
-11
lines changed

iot/api-client/manager/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
<dependency>
6262
<groupId>com.google.apis</groupId>
6363
<artifactId>google-api-services-cloudiot</artifactId>
64-
<version>v1-rev20170922-1.22.0</version>
64+
<version>v1-rev49-1.25.0</version>
6565
</dependency>
6666
<dependency>
6767
<groupId>com.google.cloud</groupId>
@@ -81,7 +81,7 @@
8181
<dependency>
8282
<groupId>com.google.api-client</groupId>
8383
<artifactId>google-api-client</artifactId>
84-
<version>1.22.0</version>
84+
<version>1.23.0</version>
8585
</dependency>
8686
<dependency>
8787
<groupId>commons-cli</groupId>

iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/DeviceRegistryExample.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
import com.google.api.services.cloudiot.v1.model.ListDeviceStatesResponse;
3535
import com.google.api.services.cloudiot.v1.model.ModifyCloudToDeviceConfigRequest;
3636
import com.google.api.services.cloudiot.v1.model.PublicKeyCredential;
37+
import com.google.api.services.cloudiot.v1.model.SendCommandToDeviceRequest;
38+
import com.google.api.services.cloudiot.v1.model.SendCommandToDeviceResponse;
3739
import com.google.api.services.cloudiot.v1.model.SetIamPolicyRequest;
3840
import com.google.cloud.Role;
3941
import com.google.cloud.pubsub.v1.TopicAdminClient;
@@ -721,6 +723,42 @@ public static void setIamPermissions(
721723
}
722724
// [END iot_set_iam_policy]
723725

726+
/** Send a command to a device. **/
727+
// [START send_command]
728+
public static void sendCommand(
729+
String deviceId, String projectId, String cloudRegion, String registryName, String data)
730+
throws GeneralSecurityException, IOException {
731+
GoogleCredential credential =
732+
GoogleCredential.getApplicationDefault().createScoped(CloudIotScopes.all());
733+
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
734+
HttpRequestInitializer init = new RetryHttpInitializerWrapper(credential);
735+
final CloudIot service = new CloudIot.Builder(
736+
GoogleNetHttpTransport.newTrustedTransport(),jsonFactory, init)
737+
.setApplicationName(APP_NAME).build();
738+
739+
final String devicePath = String.format("projects/%s/locations/%s/registries/%s/devices/%s",
740+
projectId, cloudRegion, registryName, deviceId);
741+
742+
SendCommandToDeviceRequest req = new SendCommandToDeviceRequest();
743+
744+
// Data sent through the wire has to be base64 encoded.
745+
Base64.Encoder encoder = Base64.getEncoder();
746+
String encPayload = encoder.encodeToString(data.getBytes("UTF-8"));
747+
req.setBinaryData(encPayload);
748+
System.out.printf("Sending command to %s\n", devicePath);
749+
750+
SendCommandToDeviceResponse res =
751+
service
752+
.projects()
753+
.locations()
754+
.registries()
755+
.devices()
756+
.sendCommandToDevice(devicePath, req).execute();
757+
758+
System.out.println("Command response: " + res.toString());
759+
}
760+
// [END send_command]
761+
724762
/** Entry poit for CLI. */
725763
public static void main(String[] args) throws Exception {
726764
DeviceRegistryExampleOptions options = DeviceRegistryExampleOptions.fromFlags(args);
@@ -822,6 +860,11 @@ public static void main(String[] args) throws Exception {
822860
options.member, options.role);
823861
}
824862
break;
863+
case "send-command":
864+
System.out.println("Sending command to device:");
865+
sendCommand(options.deviceId, options.projectId, options.cloudRegion, options.registryName,
866+
options.commandData);
867+
break;
825868
default:
826869
String header = "Cloud IoT Core Commandline Example (Device / Registry management): \n\n";
827870
String footer = "\nhttps://cloud.google.com/iot-core";

iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/DeviceRegistryExampleOptions.java

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public class DeviceRegistryExampleOptions {
3131
String rsaCertificateFile = "rsa_cert.pem";
3232
String cloudRegion = "us-central1";
3333
String command = "help";
34+
String commandData = "Specify with --data";
3435
String configuration = "Specify with -configuration";
3536
String deviceId; // Default to UUID?
3637
String pubsubTopic;
@@ -43,14 +44,6 @@ public class DeviceRegistryExampleOptions {
4344
/** Construct an DeviceRegistryExampleOptions class from command line flags. */
4445
public static DeviceRegistryExampleOptions fromFlags(String[] args) {
4546
// Required arguments
46-
options.addOption(
47-
Option.builder()
48-
.type(String.class)
49-
.longOpt("pubsub_topic")
50-
.hasArg()
51-
.desc("Pub/Sub topic to create registry in.")
52-
.required()
53-
.build());
5447
options.addOption(
5548
Option.builder()
5649
.type(String.class)
@@ -74,11 +67,19 @@ public static DeviceRegistryExampleOptions fromFlags(String[] args) {
7467
+ "\n\tpatch-device-es"
7568
+ "\n\tpatch-device-rsa"
7669
+ "\n\tset-config"
77-
+ "\n\tset-iam-permissions")
70+
+ "\n\tset-iam-permissions"
71+
+ "\n\tsend-command")
7872
.required()
7973
.build());
8074

8175
// Optional arguments.
76+
options.addOption(
77+
Option.builder()
78+
.type(String.class)
79+
.longOpt("pubsub_topic")
80+
.hasArg()
81+
.desc("Pub/Sub topic to create registry in.")
82+
.build());
8283
options.addOption(
8384
Option.builder()
8485
.type(String.class)
@@ -121,6 +122,13 @@ public static DeviceRegistryExampleOptions fromFlags(String[] args) {
121122
.hasArg()
122123
.desc("Name for your Device.")
123124
.build());
125+
options.addOption(
126+
Option.builder()
127+
.type(String.class)
128+
.longOpt("data")
129+
.hasArg()
130+
.desc("The command data (string or JSON) to send to the specified device.")
131+
.build());
124132
options.addOption(
125133
Option.builder()
126134
.type(String.class)
@@ -162,6 +170,16 @@ public static DeviceRegistryExampleOptions fromFlags(String[] args) {
162170
throw new ParseException("Invalid command, showing help.");
163171
}
164172

173+
if (commandLine.hasOption("cloud_region")) {
174+
res.cloudRegion = commandLine.getOptionValue("cloud_region");
175+
}
176+
if (commandLine.hasOption("data")) {
177+
res.commandData = commandLine.getOptionValue("data");
178+
}
179+
if (commandLine.hasOption("device_id")) {
180+
res.deviceId = commandLine.getOptionValue("device_id");
181+
}
182+
165183
if (commandLine.hasOption("project_id")) {
166184
res.projectId = commandLine.getOptionValue("project_id");
167185
} else {

iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/MqttExample.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ public void deliveryComplete(IMqttDeliveryToken token) {
128128
String configTopic = String.format("/devices/%s/config", deviceId);
129129
client.subscribe(configTopic, 1);
130130

131+
String commandTopic = String.format("/devices/%s/commands/#", deviceId);
132+
client.subscribe(commandTopic, 1);
133+
131134
client.setCallback(mCallback);
132135
}
133136
// [END iot_mqtt_configcallback]
@@ -275,12 +278,21 @@ public static void main(String[] args) throws Exception {
275278
}
276279
}
277280

281+
// Wait for commands to arrive for about two minutes.
282+
for (int i = 1; i <= options.waitTime; ++i) {
283+
System.out.print(".");
284+
Thread.sleep(1000);
285+
}
286+
System.out.println("");
287+
288+
278289
// Disconnect the client if still connected, and finish the run.
279290
if (client.isConnected()) {
280291
client.disconnect();
281292
}
282293

283294
System.out.println("Finished loop successfully. Goodbye!");
295+
client.close();
284296
// [END iot_mqtt_publish]
285297
}
286298
}

iot/api-client/manager/src/main/java/com/example/cloud/iot/examples/MqttExampleOptions.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public class MqttExampleOptions {
3636
String mqttBridgeHostname = "mqtt.googleapis.com";
3737
short mqttBridgePort = 8883;
3838
String messageType = "event";
39+
int waitTime = 120;
3940

4041
/** Construct an MqttExampleOptions class from command line flags. */
4142
public static MqttExampleOptions fromFlags(String[] args) {
@@ -125,6 +126,13 @@ public static MqttExampleOptions fromFlags(String[] args) {
125126
.hasArg()
126127
.desc("Indicates whether the message is a telemetry event or a device state message")
127128
.build());
129+
options.addOption(
130+
Option.builder()
131+
.type(Number.class)
132+
.longOpt("wait_time")
133+
.hasArg()
134+
.desc("Wait time (in seconds) for commands.")
135+
.build());
128136

129137
CommandLineParser parser = new DefaultParser();
130138
CommandLine commandLine;
@@ -137,6 +145,9 @@ public static MqttExampleOptions fromFlags(String[] args) {
137145
res.deviceId = commandLine.getOptionValue("device_id");
138146
res.privateKeyFile = commandLine.getOptionValue("private_key_file");
139147
res.algorithm = commandLine.getOptionValue("algorithm");
148+
if (commandLine.hasOption("wait_time")) {
149+
res.waitTime = ((Number) commandLine.getParsedOptionValue("wait_time")).intValue();
150+
}
140151
if (commandLine.hasOption("cloud_region")) {
141152
res.cloudRegion = commandLine.getOptionValue("cloud_region");
142153
}

iot/api-client/manager/src/test/java/com/example/cloud/iot/examples/ManagerIT.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,61 @@ public void testMqttDeviceConfig() throws Exception {
448448
}
449449
}
450450

451+
@Test
452+
public void testMqttDeviceCommand() throws Exception {
453+
final String deviceName = "rsa-device-mqtt-commands";
454+
topic = DeviceRegistryExample.createIotTopic(
455+
PROJECT_ID,
456+
TOPIC_ID);
457+
DeviceRegistryExample.createRegistry(CLOUD_REGION, PROJECT_ID, REGISTRY_ID, TOPIC_ID);
458+
DeviceRegistryExample.createDeviceWithRs256(
459+
deviceName, RSA_PATH, PROJECT_ID, CLOUD_REGION, REGISTRY_ID);
460+
461+
// Device bootstrapped, time to connect and run.
462+
String[] testArgs = {
463+
"-project_id=" + PROJECT_ID,
464+
"-registry_id=" + REGISTRY_ID,
465+
"-cloud_region=" + CLOUD_REGION,
466+
"-device_id=" + deviceName,
467+
"-private_key_file=" + PKCS_PATH,
468+
"-wait_time=" + 10,
469+
"-algorithm=RS256"
470+
};
471+
472+
Thread deviceThread = new Thread() {
473+
public void run() {
474+
try {
475+
com.example.cloud.iot.examples.MqttExample.main(testArgs);
476+
} catch (Exception e) {
477+
// TODO: Fail
478+
System.out.println("Failure on Exception");
479+
}
480+
}
481+
};
482+
deviceThread.start();
483+
484+
Thread.sleep(500); // Give the device a chance to connect
485+
com.example.cloud.iot.examples.DeviceRegistryExample.sendCommand(
486+
deviceName, PROJECT_ID, CLOUD_REGION, REGISTRY_ID, "me want cookie!");
487+
488+
deviceThread.join();
489+
// End device test.
490+
491+
// Assertions
492+
String got = bout.toString();
493+
System.out.println(got);
494+
Assert.assertTrue(got.contains("Finished loop successfully."));
495+
Assert.assertTrue(got.contains("me want cookie"));
496+
Assert.assertFalse(got.contains("Failure on Exception"));
497+
498+
// Clean up
499+
DeviceRegistryExample.deleteDevice(deviceName, PROJECT_ID, CLOUD_REGION, REGISTRY_ID);
500+
DeviceRegistryExample.deleteRegistry(CLOUD_REGION, PROJECT_ID, REGISTRY_ID);
501+
try (TopicAdminClient topicAdminClient = TopicAdminClient.create()) {
502+
topicAdminClient.deleteTopic(topic.getNameAsTopicName());
503+
}
504+
}
505+
451506
@Test
452507
public void testMqttDeviceEvents() throws Exception {
453508
final String deviceName = "rsa-device-mqtt-events";

0 commit comments

Comments
 (0)