Filter results by

Developing With SAMI: Let’s Bring Your Data Together

This is Part 3 of a series of articles exploring the possibilities of SAMI from a developer’s perspective. In this article, we will enhance the iOS app that we built in Part 2. The newer version illustrates how to utilize the data exchange platform SAMI to easily get data from diverse sources.

Read Part 1: Making the Connection, Part 2: A Mobile App That Pulls Weight Data and the follow-up post, Give Your App a Brain

I think this article is great and you should read it from top to bottom. However, for the impatient ones, you can skip to your preferred sections using the following links:

Introduction

In Part 2, I built the first version of my app SAMIHMonitor. This iOS app can show a user his historical weight data from a Withings scale using SAMI. Now I want to test that SAMI gives this app a way to access a user’s data from different data sources.

To validate this, I will create a new device type for SAMI to recognize. In SAMI terminology, any data source is called a device, and a device type is a category of device. I will name my new device type “SAMI Example Calorie Tracker”, or Calorie Tracker for short. A device of this type can be referred to simply as a calorie tracker.

Version two of SAMIHMonitor allows the user to record his calories and store them in SAMI, and later to view historical data from both his Withings scale and his calorie tracker. This version also illustrates how to programmatically add a device to the user’s profile in SAMI, which eliminates the need for the user to do so manually via the User Portal.

Demo: Record and view your calories

Here’s a preview of how the second version of the app will work. I mainly walk through the steps that reveal the new functionalities. In addition, you can watch the below video:

  • Start SAMIHMonitor in iOS Simulator – iPhone 6.
  • Click “CONNECT” and be presented with the login screen.
  • Enter your SAMI account credentials to login. Note that these are the same credentials used in the Part 2 demo. They are also your Samsung account credentials.
  • Click “Allow” when the app asks for permission to access calorie tracker data. This screen appears when you use the new version of the app for the first time.
  • After you finish the whole authentication flow, you will see a user information screen like the following: SAMIHMonitor V2 The user info screen lists your user ID, full name, email, SAMI account creation time, etc.
  • Click the “Show Calories” button. Your first time clicking this button, an alert pops up. You are prompted to register the phone as a calorie tracker device. Click “OK”. SAMIHMonitor V2
  • After registering, click “Show Calories” again. You will see the data screen, which lists calories and corresponding dates as follows. The screen will initially be empty, since you have not created any calorie data yet. SAMIHMonitor V2
  • Click “Add Data” button to add a new entry. On the “New Data” screen, fill in the form. Provide the number of calories, the corresponding date, and comments (if any). SAMIHMonitor V2
  • Click “Add to SAMI”. Then the filled data will be sent to SAMI. An alert will pop up to notify you that data was sent successfully.
  • Use the back arrow on the top-left to navigate back to the data screen. You can pull down on the data screen to refresh. Your newly added calorie data will appear.
  • Again, use the back arrow on the top-left to navigate back to the user info screen. Click “Show Weight” to see historical weight data from your Withings device, or click “logout” to logout from SAMI.

Installation and setup

In Part 2, I already performed the steps to Register the app. Now I need to perform one extra step in order to use this newer version of the app.

I log into the Developer Portal using my developer account. Click “SAMIHealthMonitor APP” to open the app editor. Under “PERMISSIONS”, click the “Add Device Type” button. Select “SAMI Example Calorie Tracker” as the device type. Check “Read” and “Write” permissions for this device type. What I did was to request new permissions for this app in order to access data from the new source, a calorie tracker.

Part 2 talks about how to Prepare source files and libraries. I perform exactly the same steps here, except using the source code of SAMIHMonitor V2, not SAMIHMonitor V1.

Implementation

Hopefully, by now you have succeeded in playing with the second version of the iOS app. I am ready to dive into the implementation.

At the high level, I need to implement three new functionalities on top of the first version:

  • Create a new device type, “SAMI Example Calorie Tracker”.
  • Add the user’s phone to SAMI as a calorie tracker device.
  • Add new calorie data to SAMI to store.

Create the Calorie Tracker device type

Creating this new device type is the very first step. I should do it before starting to implement SAMIHMonitor V2. I didn’t need this step in Part 2 because SAMIHMonitor V1 only uses Withings Device, a device type already available on SAMI. The purpose of this section is to take you through creating a new device type for your own application. If you’re just interested in the SAMIHMonitor V2 code, feel free to skip to the next section.

SAMI is designed to communicate with any device regardless of how data is structured. It uses the Manifest to achieve this. To create my new Calorie Tracker device type, I need to write its Manifest and then submit it in the Developer Portal for approval. After the Manifest is approved, I can publish the Manifest from the Developer Portal. After it is published, Calorie Tracker can be used as a device type by any app working with SAMI.

To create the Manifest, I must first design the data format. My data is very simple, per the example below. It is JSON with two fields: daily caloric intake, and comments. This data is further wrapped into a message that is sent to SAMI. I will use the timestamp field of a message to record the corresponding date of a calorie data point, which is why I do not need this field in the data. Another benefit of doing this is that the messages retrieved from SAMI will be ordered using the timestamps of the messages, which makes perfect sense when showing historical calorie data.

1 {
2     "calories":2000,
3     "comments":"put comments here if needed"
4 }

Next, I follow the Manifest documentation to write my Manifest in Groovy. Basically, I override two methods of the base class Manifest and use utility methods from the Manifest SDK to simplify the implementation.

 1 import groovy.json.JsonSlurper
 2 import com.samsung.sami.manifest.Manifest
 3 import com.samsung.sami.manifest.fields.*
 4 import static com.samsung.sami.manifest.fields.StandardFields.*
 5 import static com.samsung.sami.manifest.groovy.JsonUtil.*
 6 
 7 public class CalorieTrackerManifest implements Manifest {
 8     static final CALORIES_INT = CALORIES.alias(Integer.class);
 9     static final COMMENTS = new FieldDescriptor("comments", String.class)
10 
11     @Override
12     List<Field> normalize(String input) {
13         def slurper = new JsonSlurper()
14         def json = slurper.parseText(input)
15 
16         def fields = []
17 
18         addToList(fields, json, "calories", CALORIES_INT)
19         addToList(fields, json, COMMENTS)
20 
21         return fields
22     }
23 
24     @Override
25     List<FieldDescriptor> getFieldDescriptors() {
26         return [CALORIES_INT, COMMENTS]
27     }
28 }

When receiving messages sent by a calorie tracker, SAMI uses the above Manifest to parse the data and store it properly for future retrieving.

Before submitting my Manifest on the Developer Portal, I need to test it for correctness. I use the sample Maven project as a template to create my own Maven project. You can get my Maven project from github and then follow Quick start to build and run the test. Below is a unit test in this project:

 1 import com.samsung.sami.manifest.fields.Field;
 2 import com.samsung.sami.manifest.test.ManifestTest;
 3 
 4 public class TestCalorieTracker extends ManifestTest {
 5     @Test
 6     public void testCalorieTracker() throws IOException {
 7         String manifestPath = "/manifests/CalorieTrackerManifest.groovy";
 8         String dataPath = "/data/CalorieTrackerData.json";
 9         runManifestTest(manifestPath, dataPath);
10     }
11 
12     private void runManifestTest(String manifestPath, String dataPath) throws IOException {
13         Map<String, Field> fields = runGroovyManifest(manifestPath, dataPath);
14 
15         printManifestRunResults(fields);
16         Assert.assertNotNull(fields);
17         Assert.assertFalse(fields.isEmpty());
18  
19         // Verify that the Manifest produces the correct value for each field
20         Assert.assertEquals(2000, fields.get("calories").getValue());
21         Assert.assertEquals("put comments here if needed", fields.get("comments").getValue());
22     }
23 
24     private void printManifestRunResults(Map<String, Field> fields) {
25         for (Field field : fields.values())
26             System.out.println(field.toString());
27     }
28 }

The unit test uses CalorieTrackerManifest.groovy to parse the sample JSON data and then verifies the value of each field.

Now I submit the Manifest by following the instructions for Creating a device type. When submitting in the Developer Portal, I give my device type the name “SAMI Example Calorie Tracker”, and a unique name io.samsungsami.example.calorieTracker.

I receive an approval notification within a day. Then I go to the Developer Portal, click “SAMI Example Calorie Tracker”, and then the “Publish” button. The status of this device type is updated to “PUBLISHED”. From now on, this device type is visible to other developers on the User Portal and Developer Portal, and can be used by any app, including SAMIHMonitor V2. Now I am ready to talk about the implementation of SAMIHMonitor V2.

Register your phone as a calorie tracker

A user uses SAMIHMonitor to record his caloric intake for specific dates. This data is stored in SAMI for future retrieving by SAMIHMonitor or other apps that have the user’s permission. As the first step to achieve this, the app adds the user’s phone as a Calorie Tracker device type. Please note that you can add a user device programmatically using an API call, instead of asking a user to manually do that in the User Portal.

UserInfoViewController makes three API calls to SAMI to add a calorie tracker. The first call is to get the device type ID of Calorie Tracker, using its device type name: “SAMI Example Calorie Tracker”. The second call is to get the list of devices that the user owns. Then this list is parsed to find out if the user already has a calorie tracker. If the answer is YES, calorie data entered on the phone is for this identified calorie tracker. If the answer is NO, UserInfoViewController makes a third API call to SAMI to add the current phone as a calorie tracker for a user.

I won’t get into the details of the first and second API calls, since they are similar to what I did for the Withings device in Part 2 of the series. Please consult Get the Withings device info in Part 2.

The following code illustrates the third API call that adds the phone as a calorie tracker for the user. Specifically, it uses the addDeviceWithCompletionBlock method of the SamiDevicesApi class in the SAMI iOS SDK.

 1 - (void)registerPhoneAsCalorieTracker {
 2     UserSession *session = [UserSession sharedInstance];
 3     NSString* authorizationHeader = session.bearerToken;
 4     
 5     SamiDevicesApi * api = [[SamiDevicesApi alloc] init];
 6     [api addHeader:authorizationHeader forKey:kOAUTHAuthorizationHeader];
 7     
 8     SamiDevice* deviceToRegister = [[SamiDevice alloc] init];
 9     deviceToRegister.name = kDeviceNameCalorieTracker;
10     deviceToRegister.uid = session.user._id;
11     deviceToRegister.dtid = session.calorieTrackerDeviceTypeId;
12     
13     [api addDeviceWithCompletionBlock:deviceToRegister completionHandler:^(SamiDeviceEnvelope *output, NSError *error) {
14         if (!error) {
15             calorieTracker_ = (SamiDevice*)output.data;
16             NSLog(@"Registed Calorie Track with device id %@", calorieTracker_._id);
17         } else {
18             NSLog(@"Adding device failed with error %@", error);
19         }
20     }];
21     
22 }

After the calorie tracker is added, UserInfoViewController remembers this device in its data member calorieTracker_. Later on, it passes this device object around to other objects, since the API calls to send and get calorie data to and from SAMI requires the information of this device.

Add calorie data

AddCalorieDataViewController implements the logic that presents a form to a user to fill in new calorie data, and then sends the data to SAMI. In the following code, “Calorie taken” and “Comments” fields in the form are used to set message.data. Method setMessageTimestamp validates the date field in the form and then sets message.ts. To send the data, I call the postMessageWithCompletionBlock method of the SamiMessagesApi class in the SDK.

 1 - (IBAction)addMessage:(id)sender {
 2     NSString* authorizationHeader = [UserSession sharedInstance].bearerToken;
 3     
 4     SamiMessagesApi * api2 = [SamiMessagesApi apiWithHeader:authorizationHeader key:kOAUTHAuthorizationHeader];
 5     
 6     SamiMessage *message = [[SamiMessage alloc] init];
 7     message.sdid = device_._id;
 8     message.data = @{ @"calories": @([self.caloriesField.text integerValue]),
 9                       @"comments": self.commentsField.text};
10     BOOL succeed = [self setMessageTimestamp:message];
11     if (!succeed) {
12         UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
13                              message:@"You entered an incorrect date. Please correct it."
14                              delegate:nil
15                              cancelButtonTitle:@"OK"
16                              otherButtonTitles:nil];
17         [alert show];
18         return;
19     }
20     
21     [api2 postMessageWithCompletionBlock:message completionHandler:^(SamiMessageIDEnvelope *output, NSError *error) {
22         NSLog(@"[api postMessageWithCompletionBlock] output: %@ \n error: %@", output, error);
23         if (!error) {
24             UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Success"
25                                  message:[@"Message added " stringByAppendingString:output.data.mid]
26                                  delegate:nil
27                                  cancelButtonTitle:@"OK"
28                                  otherButtonTitles:nil];
29             [alert show];
30         } else {
31             NSLog(@"%@", error);
32             
33             UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error"
34                                  message:error.localizedDescription
35                                  delegate:nil
36                                  cancelButtonTitle:@"OK"
37                                  otherButtonTitles:nil];
38             [alert show];
39         }
40     }];
41 }

Get calorie data and units

I discussed how to make SAMI API calls to get data and data units in Get the weight data and Get the weight units of Part 2. This works the same for calorie data. I generalize the logic in DataTableViewController so that it shows the list of data from both a Withings scale and a calorie tracker.

What comes next?

Developing SAMIHMonitor V2 convinced me that a third-party app could create a service that is highly relevant to individual users, by utilizing users’ data from diverse data sources in SAMI. It is straightforward to implement data-exchange functionalities using SAMI API calls. SAMIHMonitor V1 and V2 illustrate how to discover a user’s devices, add a user’s device, send data to SAMI for storage and future exchange, and retrieve data from different data sources through SAMI. I hope you are as excited as I am about SAMI’s potential for developing cool applications.

This article concludes this series. However, I will continue to build more comprehensive sample applications to explore SAMI, and write about them in future articles. Some examples in my mind: an Android application that reads and sends sensor data from a wearable to SAMI; and a system that includes mobile, web front-end, and back-end components.

Stay tuned for more development blog posts by joining our mailing list at http://developer.samsungsami.io/ to receive new blog notifications.

Read Part 1: Making the Connection and Part 2: A Mobile App That Pulls Weight Data and the follow-up post, Give Your App a Brain

Top image: Charis Tsevis

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, new blog posts and more!

By providing your contact information, you agree to our Privacy Policy and Terms of Use, confirm you are an adult 18 years or older, and authorize Samsung™ ARTIK to contact you by email with information about Samsung™ ARTIK products, events, and updates for Samsung IoT Solutions. You may unsubscribe at any time by clicking the link provided in our communications.