User Tools

Site Tools


ipca

IoTivity Procedural Client API (IPCA)

IoTivity is an open source software framework enabling seamless device to device connectivity for emerging needs of Internet of Things.

Client side application developers and code generation tools (where developers use codes that are automatically generated from RAML or Swagger definition from oneiota or from device introspection) need simple set of APIs with some isolation from the wire protocol to the IoT devices. The base IoTivity C APIs and the enhanced C++ APIs (OCResource and OCPlatform) provide useful RESTful CRUDN APIs to connect with devices. IPCA builds on them by adding procedural programming interfaces that application developers are familiar with. Readers are encouraged to learn more about the C & C++ APIs for the underlying concepts <TBD link to C/C++ APIs>.

At the high level, IPCA provides the following set of functions to developers:

  • APIs to discover devices. Application provides a list of resource types that a device implements.
  • APIs for working with data. From primitive data types (integer, double, bool, string) to arrays and complex data structures of the primitive data types.
  • APIs for connecting and working with devices. Support for setting and getting property values; Observing resources for value changes; Creating and Deleting resources on remote device.
  • APIs for working with IoTivity security features: Application registers for password display callback (which is typically required to work with an IoTivity Onboarding Tool) and for password input (when connecting to devices that support Multiple Ownership Transfer).

Below are more detailed descriptions of each area with code snippets to illustrate how an application uses the APIs. Readers are referred to header file service\IPCA\inc\ipca.h for API descriptions and samples in service\IPCA\samples that show how to use the APIs. See <TBD> these instructions on how to run the samples.

Device Discovery

The application specifies the resource types (can also be empty string for any resource type) for devices it is interested to discover. The code below shows an application starting discovery for devices that implement two resource types as defined in resourceTypes[] array. The application provided callback, DiscoverDevicesCallback(), is called when a device implements the required resource types is discovered. At any time, the application can cancel the discovery process by calling IPCACloseHandle(). The application obtains ipcaAppHandle by calling the IPCAOpen().

  const char* resourceTypes[] = {
      "x.oem.elevator.status",
      "x.oem.elevator.operation" 
  };
  
  IPCAHandle discoverDeviceHandle;
  
  status = IPCADiscoverDevices(
                  ipcaAppHandle,
                  &DiscoverDevicesCallback,
                  this,
                  resourceTypes,
                  _countof(resourceTypes),
                  &discoverDeviceHandle);
                  
  <application performs tasks>;
  
  // Stop discovery.
  IPCACloseHandle(discoverDeviceHandle, &CloseDiscoveryComplete, 0);

When a device is discovered, the application receives the unique ID of the device. The application uses this ID to connect to the device and to perform CRUDN requests.

The code below shows the application receiving device discovery callback and requesting for device info. The application may receive IPCA_DEVICE_STOPPED_RESPONDING if the device goes offline (/oic/res stopped responding) during discovery.

  void DiscoverDevicesCallback(void* context, 
                               IPCADeviceStatus deviceStatus,
                               const IPCADiscoveredDeviceInfo* deviceInfo)
  {
      if (deviceStatus == IPCA_DEVICE_DISCOVERED) 
      {         
          IPCADeviceHandle deviceHandle;
          IPCAStatus status = IPCAOpenDevice(ipcaAppHandle, deviceInfo->deviceId, &deviceHandle);
          if (status != IPCA_OK)
          {
             // Handle errors here.
          }
          
          IPCADeviceInfo* deviceInfo;
          status = IPCAGetDeviceInfo(deviceHandle, &deviceInfo); 
          if (status != IPCA_OK)
          {
              // Handle errors here.
          }
          
          // ... use device info information	
          
          IPCAFreeDeviceInfo(deviceInfo);
      }
  }

Data APIs

The code below shows creation of a data structure that holds an integer data for a property with the following name “x.oem.TargetFloor”.

  IPCAPropertyBagHandle propertyBagHandle;
  
  status = IPCAPropertyBagCreate(&propertyBagHandle);
  
  if (status != IPCA_OK)
  {
      // Handle errors here.
  }
  
  status = IPCAPropertyBagSetValueInt(propertyBagHandle, "x.oem.TargetFloor", 10);

Connecting and Working With Devices

The sample code below shows how an application sends the property bag created above to the target device. As the server may take some time to complete the operation, the application can cleanly cancel the request at any time using IPCACloseHandle(). Cancelling the request allows the application to clean up any shared resources (e.g., the context).

  status = IPCASetProperties(
               deviceHandle, 
               &SetPropertiesCompleteCallback, /* callback when request is complete */
               context,  
               "/oem/elevator", /* resource path of the resource */
               nullptr, /* use default interface */
               nullptr, /* not providing specific resource type */
               propertyBagHandle,
               &setPropertyHandle);
               
  if (status != IPCA_OK)
  {
     // Handle errors here.
  }

When server responds to the request, the set complete callback is called with a status.

  void SetPropertiesCompleteCallback(IPCAStatus result, 
                                     void* context, 
                                     IPCAPropertyBagHandle propertyBagHandle)
  {   
      if (result == IPCA_OK)
      {
          // some code to indicate successful setting of property.
      }
      else
      {
          // Handle errors here.
      }
  } 

It is possible that server is offline or fails to respond. For this case, IPCA has built in timer to complete the callback with IPCA_REQUEST_TIMEOUT. For outstanding Observe request on a resource on this offline device, IPCA indicates IPCA_DEVICE_APPEAR_OFFLINE as the device fails to respond to periodic ping to its \oic\d resource.

APIs for Security

An IoTivity application can be security onboarded and then, by the device owner, given access control to a device (this is the scenario for using IoTivity Onboarding Tool, OBT) or the application itself can directly access the target device by demonstrating that it knows the shared password with the device (this is the scenario for devices supporting Multiple Ownership Transfer, MOT).

The code below shows an application with ability to handle both scenarios. It does that by providing 2 callbacks in the IPCASetPasswordCallbacks() API for displaying password (in OBT case) and for providing password (in MOT case).

  // This application supports Onboarding authentication based on either:
  //
  // - Displaying a password, or
  // - Having the user provide a password.
  //
  // Therefore, set-up both types of callbacks.
  status = IPCASetPasswordCallbacks(
                 g_ipcaAppHandle, 
                 &ProvidePasswordCallback, 
                 &DisplayPasswordCallback,
                 nullptr);
  
  if (status != IPCA_OK)
  {
      // Handle errors here. This application cannot be Onboarded,
      // and therefore might fail to access some of the Device resources.
  }

When an onboarding tool provisions the application, the DisplayPasswordCallback is called. The device owner is required to key in the password displayed by the application into the OBT app.

For direct access to a device that supports MOT, the application calls IPCARequestAccess() when it gets IPCA_ACCESS_DENIED for operation it requests. For example, if the IPCASetProperties() in earlier example were to return IPCA_ACCESS_DENIED as shown below, the application could call IPCARequestAccess() which would start the handshake with the MOT device resulting in the ProvidePasswordCallback() being called to the application (as the MOT device requests for shared password that the application or the user of the application must have knowledge of).

  void SetPropertiesCompleteCallback (
           IPCAStatus result, 
           void* context, 
           IPCAPropertyBagHandle propertyBagHandle)
  {
      PELEVATOR_REQUEST request = static_cast<PELEVATOR_REQUEST>(context);
      IPCAStatus requestAccessStatus = IPCA_OK;
      
      switch (result)
      {
          case IPCA_OK:
              // GET request succeeded.
              break;
          
          case IPCA_ACCESS_DENIED:
              // Asynchronously request access to the target device. IPCAProvidePasswordCallback or 
              // IPCADisplayPasswordCallback might be invoked by IPCA while processing this request.
              requestAccessStatus = IPCARequestAccess(
                                          request->deviceHandle, 
                                          request->resourcePath,
                                          AccessRequestCompletedCallback,
                                          &g_request, // Context for the access request completion callback.
                                          nullptr);   // The application doesn't plan to cancel this request.
                                          
              if (requestAccessStatus == IPCA_OK)
              {
                  // Notify the user that the application doesn't have access to the target Device.
                  // Suggest them to initiate Onboarding, using the separate Onboarding app.
              }
              else
              {
                  // Handle errors here.
              }
              break;
              
          default:
              // Handle other errors here.
              break;
      }
  }

Below is an example of AccessRequestCompletedCallback() implementation. Receiving IPCA_SECURITY_UPDATE_REQUEST_FINISHED status indicates that the application should now be authorized to work with the target device.

  void AccessRequestCompletedCallback(
                  IPCAStatus completionStatus, 
                  void* context)
  {
      PELEVATOR_REQUEST request = static_cast<PELEVATOR_REQUEST>(context);
      IPCAStatus newRequestStatus = IPCA_OK;
      
      switch (completionStatus)
      {
          case IPCA_SECURITY_UPDATE_REQUEST_FINISHED:
              // Security settings of the current application have been updated. Ask the user
              // if they want to retry the GET request. If a retry is desired:
              newRequestStatus = IPCAGetProperties(
                                     request->deviceHandle, 
                                     &GetPropertiesCallback, 
                                     &request,               // completion context
                                     request->resourcePath,
                                     NULL, // use default interface
                                     request->resourceType,
                                     &request->getRequestHandle);
                                     
              if (newRequestStatus != IPCA_OK)
              {
                  // Handle errors here.
              }
              break;
              
          case IPCA_SECURITY_UPDATE_REQUEST_INCORRECT_PASSWORD:
              // Ask the user if they want to try security Onboarding again. If that's the case,
              // call IPCARequestAccess again.
              break;
              
          case IPCA_SECURITY_UPDATE_REQUEST_NOT_SUPPORTED:
              // Unable to update security settings for the current application. Future requests
              // to the same Device will most likely fail.
              break;
              
          case IPCA_SECURITY_UPDATE_REQUEST_FAILED:         // Execute the default branch below.
          default:
             // Updating the security settings has been attempted, but failed for reasons
             // other than the ones above.
             
             // Ask the user if they want to try security Onboarding again. If that's the case,
             // call IPCARequestAccess again.
             break;
      }
  } 
ipca.txt · Last modified: 2017/03/08 18:45 by Soemin Tjong