Filter results by

Making the Perfect Remote Control in Five Steps

Recently we developed an Android application to get data from a nearby BLE device and send it to SAMI. In this post, we show you how you can communciate with your devices using SAMI as a remote control. Our demo application uses SAMI to retrieve the status of a user’s devices and send commands to these devices.

Before starting mobile development, we recommend playing with the SAMI API in the API Console. For more basics, see Your first Android app in the SAMI documentation.

Below are the steps we will take here:

  1. Registering the application in SAMI
  2. Preparing source files and libraries
  3. Authenticating the user in SAMI and getting the user’s profile
  4. Retrieving device information
  5. Sending commands to devices

Registering the application in SAMI

First, register your mobile application in the SAMI Developer Portal.

SAMIRemoteControl

Follow these instructions to add your application. Select the following for the Android application:

  • Set “Redirect URL” to android-app://redirect.
  • Choose “Client credentials, auth code, implicit” OAuth2 authentication mode.
  • Check the “Read” permissions on the user profile.
  • Add “READ” and “WRITE” permissions to the types of devices that you want to control. Click the “Add Device Type” button. Choose, for example, “zz_OLD_philips_00000000001”, “Jawbone”, and “Kwikset Lock” as device types.

SAMI generates a client ID (we also call this an application ID) and a client secret for the application. Make a note of the client ID.

SAMIRemoteControl

Preparing source files and libraries

Get the source files of SAMIRemoteControl from GitHub.

Here are the prerequisites:

Download and build SAMI’s Java/Android SDK Version 1. The library JAR files are generated under the target and target/lib directories of the SDK Maven project.

Copy all library JAR files to app/libs of SAMIRemoteControl. Import SAMIRemoteControl as Non-Android studio project. Use the client ID (obtained when registering the app in the Developer Portal) to replace YOUR CLIENT APP ID in SamiHelper.java.

Now build and deploy the APK to an Android phone.

Authenticating the user in SAMI and getting the user’s profile

On SAMI, user authentication relies on the OAuth2 mechanism. After the authentication flow finishes, SAMIRemoteControl obtains an access token and uses it to make SAMI API calls on behalf of the user.

SAMIRemoteControl

The user will first be invited to log into the service, via a Samsung Accounts authorization page, and grant the application access. Finally, they are redirected to your client interface.

This is done by displaying the authorization page in a WebView component and catching the SAMI access token, once the WebView current URL contains an access_token (the WebView URL loading is overridden). Next, the application switches to another Android activity.

SAMIRemoteControl

Have a look at the login method in LoginActivity.java:

 1 private void login() {
 2         webView = (WebView) findViewById(R.id.webView);
 3         webView.getSettings().setJavaScriptEnabled(true);
 4         webView.setWebViewClient(new WebViewClient() {
 5             @Override
 6             public boolean shouldOverrideUrlLoading(WebView view, String uri) {
 7                 // The login is successful
 8                 if (uri.contains("access_token=")) {
 9                     // Extracts SAMI access token
10                     String[] sArray = uri.split("access_token=");
11                     String accessToken = sArray[1];

Before making any SAMI API call, we have to first dynamically instantiate a SAMI API object (UsersApi, DevicesApi, DeviceTypesApi, MessagesApi). Then, we create an asynchronous task by providing the SAMI API object to it.

 1 UsersApi api = new UsersApi();
 2 api.setBasePath(SamiHelper.SAMIHUB_BASE_PATH);
 3 api.getInvoker().addDefaultHeader("Authorization", "bearer "
 4         + SamiHelper.getAccessToken());
 5 // If the access token is valid : it will be possible to gather User data from the API
 6 try {
 7     new CallUsersApiInBackground().execute(api).get();
 8 } catch (InterruptedException e) {
 9 } catch (ExecutionException e) {
10 }

In the above example, the asynchronous task CallUsersApiInBackground calls the SAMI API in the background. The asynchronous task is a nested class which contains two parts (methods): one to call the API (doInBackground), and another one to process the results (onPostExecute). The SAMI API creates the HTTP JSON requests and parses the corresponding response.

Let’s see how the user name is retrieved. First, UsersApi is invoked:

 1 class CallUsersApiInBackground extends AsyncTask<UsersApi, Void, UserEnvelope> {
 2     @Override
 3     protected UserEnvelope doInBackground(UsersApi... apis) {
 4         UserEnvelope retVal = null;
 5         try {
 6             retVal = apis[0].self();
 7         } catch (ApiException e) {
 8 
 9         }
10         return retVal;
11     }

Then, once the result is received, the user’s full name is extracted:

1 @Override
2     protected void onPostExecute(UserEnvelope result) {
3         if ((result != null) && (result.getData() != null)
4                 && (result.getData().getFullName() != null)
5                 && (result.getData().getFullName().length() > 0)) {
6             SamiHelper.setUserId(result.getData().getId());
7         }
8     }
9 }

Retrieving device information

The Manifest is a descriptor of any device in SAMI. It consists of a Groovy script which specifies how incoming raw data from a device should be normalized (interpreted) by SAMI.

Let’s retrieve the user’s device information:

  • The device list
  • For each device:
    • The device name and device ID
    • The corresponding Manifest
    • The device status, for each field of the Manifest:
      • The field name
      • The latest value
      • The field unit

First, we get the user device list, thanks to UsersApi in DeviceListActivity.java (excerpt):

 1 class CallUsersApiInBackground extends android.os.AsyncTask<UsersApi, Void, DevicesEnvelope> {
 2 
 3     @Override
 4     protected Object[] doInBackground(UsersApi... apis) {
 5         DevicesEnvelope result = null;
 6         try {
 7             result = apis[0].getUserDevices(0, 100, true, SamiHelper.getUserId());
 8             
 9         } catch (ApiException e) {
10 
11         }
12         return result;
13     }

Then for each device, we fetch its Manifest with DeviceTypesApi. Finally, for each field of its Manifest, we get the last normalized message (for this device and that field) with MessagesApi, so that we have the latest known status of the device.

SAMIRemoteControl

Look at DeviceActivity.java (excerpt) to understand how this is done. First, a call is made to DeviceTypesApi for getting all the Manifest properties:

 1 class CallDeviceTypesApiInBackground extends android.os.AsyncTask<DeviceTypesApi, Void, ManifestPropertiesEnvelope> {
 2 
 3         @Override
 4         protected ManifestPropertiesEnvelope doInBackground(DeviceTypesApi... apis) {
 5             ManifestPropertiesEnvelope reval = null;
 6             try {
 7                 reval = apis[0].getManifestProperties(dtid, manifestVersion.toString());
 8             } catch (ApiException e) {
 9             }
10             return reval;
11         }

Next, for each field of the Manifest, its name and unit are stored. Then, MessagesApi is called to deduce the device status:

 1 @Override
 2 protected void onPostExecute(ManifestPropertiesEnvelope result) {
 3     // All fields properties
 4     HashMap fieldsProperties = (LinkedHashMap) result.getData().getProperties().get("fields");
 5 
 6     // Current field properties
 7     HashMap fieldProperties;
 8     String unit;
 9     for (Object key : fieldsProperties.keySet()) {
10         // Add the current field
11         fields.add(key.toString());
12         // Extract current field properties
13         fieldProperties = (HashMap) fieldsProperties.get(key);
14 
15         // Add the unit
16         try {
17             unit = fieldProperties.get("unit").toString();
18             units.add(unit);
19 
20         } catch (Exception e) {
21             units.add("");
22         }
23     }
24     MessagesApi msgApi = new MessagesApi();
25     msgApi.setBasePath(SamiHelper.SAMIHUB_BASE_PATH);
26     msgApi.getInvoker().addDefaultHeader("Authorization", "bearer " + SamiHelper.getAccessToken());
27     new CallMessageApiInBackground().execute(msgApi);
28 }

Finally, MessagesApi is called to find the latest known values of the current device for all fields, and the results are stored simultaneously.

 1 class CallMessageApiInBackground extends android.os.AsyncTask<MessagesApi, Void, List<NormalizedMessagesEnvelope>> {
 2 
 3         @Override
 4         protected List<NormalizedMessagesEnvelope> doInBackground(MessagesApi... apis) {
 5             // For all the DeviceType manifest field, get the corresponding value
 6             // for the current device
 7             List<NormalizedMessagesEnvelope> results = new ArrayList<NormalizedMessagesEnvelope>();
 8             for (String field : fields)
 9                 try {
10                     results.add(apis[0].getLastNormalizedMessages(1, did, field));
11                 } catch (ApiException e) {
12                     // No value are available
13                     results.add(null);
14                 }
15 
16             return results;
17         }
18 
19     }

Sending commands to devices

Now, the fun part begins! Let’s drive some devices, thanks to SAMI.

Sending commands to a device in SAMI is really simple: it just consists of sending a message to this device using MessagesApi by specifying the destination device ID (ddid) and the intended command in JSON (data). As the user has granted read and write permissions to our application, the message’s token and source device ID (sdid) values do not matter.

For this demo, we implemented a fallback (an editText and a send button) where the user can enter and send any command manually. Once, the user has entered the command, and pressed the send button, the command will be parsed, and the message data will be set and sent to SAMI. (Note that SAMI now provides Manifest actions that define which commands can be sent to a device!)

Again, in DeviceActivity.java the fallback form is built, and the command message is prepared (excerpt):

 1 public class DeviceActivity extends Activity {
 2     // Message stencil
 3     Message sendCommandMessage = null;
 4     
 5     public void addFallback() {
 6 
 7         button.setOnClickListener(new View.OnClickListener() {
 8             public void onClick(View v) {
 9 
10                 MessagesApi msgApi = new MessagesApi();
11                 msgApi.setBasePath(SamiHelper.SAMIHUB_BASE_PATH);
12                 msgApi.getInvoker().addDefaultHeader("Authorization", "bearer " + SamiHelper.getAccessToken());
13 
14                 sendCommandMessage = new Message();
15                 sendCommandMessage.setSdid(did);
16                 sendCommandMessage.setDdid(did);
17                 sendCommandMessage.setToken("fakeToken");
18 
19                 try {
20                     Map<String, Object> messageData = mapper.readValue(editText.getText().toString(), Map.class);
21                     sendCommandMessage.setData(messageData);
22                     new CallPostMessageApiInBackground().execute(msgApi);
23 
24                 } catch (IOException e) {
25                 }
26 
27             }
28         });
29 
30     }

Finally, the command message is posted to SAMI.

 1 class CallPostMessageApiInBackground extends android.os.AsyncTask<MessagesApi, Void, MessageIDEnvelope> {
 2         @Override
 3         protected MessageIDEnvelope doInBackground(MessagesApi... apis) {
 4             MessageIDEnvelope res = null;
 5             try {
 6                 apis[0].postMessage(sendCommandMessage);
 7             } catch (ApiException e) {
 8             }
 9             return res;
10         }
11 
12     }
13 }

Here are some examples of JSON commands that can be sent to Philips Hue lights:

1 {“command”:”on”}
2 
3 {“command”:”off”}

Conclusion

Developing with Android is really easy and convenient. You just need to use the SAMI SDK, and call the API in a straightforward manner to get the status of your devices.

To complete the picture, you still need to implement the logic at the device side, which interprets the commands from SAMI. For smart devices in the market like Philips Hue lights, you normally need to connect the device to a proxy, and then program the proxy to interprete the commands.

In the future, we will blog about implementing a proxy for a smart device and an alternative (and even better) way of sending commands to remote devices, using SAMI’s new Manifest actions. Stay tuned for more development blog posts by joining our mailing list at http://developer.samsungsami.io/ to receive new blog notifications.

Top image: Bowen Chin

Get the ARTIK Newsletter

You like your news fresh! Sign up now and you will be the first to know about our latest software releases, coding tips, upcoming events, blog posts, datasheet updates, and more.

* By checking either box, you may receive notifications by phone, email, text, and/or other electronic means from Samsung Semiconductor, Inc. and its affiliates. If you choose to receive partner notifications, we may forward your contact information to our partners. You may unsubscribe from these services at any time by clicking on the unsubscribe link in our communications or by submitting a request here. Please see our Privacy Policy and Terms of Use for more information about how your data is stored and used.