+

Search Tips   |   Advanced Search

Differentiating between indoor areas

We can use visible access points to identify areas in an indoor location such as a shopping mall. After transmitting this data to a server, together with the device context, we can use it for auditing, reporting, and analysis.

The process of acquiring data that identifies discrete areas in an indoor location, where the GPS signal might be poor or non-existent, involves acquiring WiFi data, and using WiFi triggers to initiate events.

  1. Scan the area to determine which access points are visible from each area that you are interested in, and then record the access points.

    To scan the area, create a small application that has the following elements:

    • A WiFi acquisition policy for appropriate SSIDs. In the policy, specify MAC: "*" to see each access point.

    • A data entry function, for specifying the various indoor areas of interest, and submitting the data. This data entry function calls WL.Client.transmitEvent to send the location, together with the device context, to the server for logging and subsequent analysis.

  2. Analyze the data, and use the analysis to determine which access points are visible in each of the regions.

  3. In the application, use the accessPointFilters parameter to define the same visible access points that were used previously.

  4. Define WiFi-fence triggers for each region.


Example

This example shows the use of two small applications.

The first defines which networks are to be scanned, and lets the user define named regions as the client device moves around the indoor area. For example, when the user enters the food court, they could specify that the region is called "FoodCourt". Upon leaving it, they could either clear the current region, or enter the name of the adjacent region they are entering, such as "MallEntrance5". To implement this process, adapter logic is implemented on the server side. It updates the application context with the region information and handles all received events. In this way, all the information is written out to the raw reports database, where each row includes the region name in the APP_CONTEXT column, and the visible access points under WIFI_APS.

The data can then be gathered to define triggers to implement the required application logic. For example, in the triggers that are defined at the end of the example, the two specific access points are identified, which should be visible when the device is in the food court. The example shows the identification of a global trigger for entering the mall; instead, a trigger could have been defined for each of the mall entrances based on the access points visible at each location.

Application to set up acquisition policy, including triggers - hybrid Android, iOS, and Windows Phone 8

function wlCommonInit(){
 /*
  * Use of WL.Client.connect() API before any connectivity to a MobileFirst Server is required. 
  * This API should be called once, before any other WL.Client methods that communicate with the MobileFirst Server.
  * Do not forget to specify and implement the onSuccess and onFailure callback functions for WL.Client.connect():
  *    
  *    WL.Client.connect({
  *      onSuccess: onConnectSuccess,   *      onFailure: onConnectFailure
  *    });
  *     
  */
 
 // Common initialization code goes here.
 WL.App.hideSplashScreen();
 
} 
var SSIDs = [];
function addNetworkToBeScanned(ssid) {
  if (SSIDs.indexOf(ssid) < 0)
    SSIDs.push(ssid);
}
function removeNetwork(ssid) {
  var idx = SSIDs.indexOf(ssid);
  if (idx > 0)
    SSIDs.splice(idx, 1);
}
function startScanning() {
  var filters = [];
  for (var i = 0; i < SSIDs.length; i++) {
    var ssid = SSIDs[i];
    filters.push({SSID: ssid, MAC: "*"});
 
  var policy = {
    Wifi: {
      interval: 3000,   accessPointFilters: filters
    }
 
  var triggers = {
    Wifi: {
      change: {
        type: "VisibleAccessPointsChange",     eventToTransmit: {
          event: {
            name: "moved"
          }
        }
    } 
 
  var onFailure = {
    Wifi: onWifiFailure
 
  WL.Device.startAcquisition(policy, triggers, onFailure);
}
function stopScanning() {
  WL.Device.stopAcquisition();
}
function onWifiFailure(code) {
  // Show an error message to the user... 
}
// Receives a string, indicating the name of the region
function setCurrentRegion(region) {
  WL.Server.invokeProcedure(
    {
      adapter: "HT_WifiScan", 
      procedure: "setAppContext",   parameters: [JSON.stringify({regionName: region})]
    }, {
      onSuccess: function() {
        // update UI, indicating success
      },   onFailure: function() {
        // update UI, indicating error
    }
  );
}

Application to set up acquisition policy, including triggers - native Android

Set<String> ssids = new HashSet<String>();
public void addNetworkToBeScanned(String ssid) {
  ssids.add(ssid);
}
public void removeNetwork(String ssid) {
  ssids.remove(ssid);
}
public void startScanning() throws JSONException {
  List<WLWifiAccessPointFilter> filters = new ArrayList<WLWifiAccessPointFilter>();
   for (String ssid : ssids)
    filters.add(new WLWifiAccessPointFilter (ssid, WLWifiAccessPointFilter.WILDCARD));
  WLAcquisitionPolicy policy = new WLAcquisitionPolicy().setWifiPolicy(
    new WLWifiAcquisitionPolicy().setInterval(3000).setAccessPointFilters(filters)); 
  
  WLTriggersConfiguration triggers = new WLTriggersConfiguration();
  triggers.getWifiTriggers().put(
       "change",    new WLWifiVisibleAccessPointsChangeTrigger().setEvent(new JSONObject("{name: 'moved'}")));
  WLAcquisitionFailureCallbacksConfiguration failures = new WLAcquisitionFailureCallbacksConfiguration(); 
  failures.setWifiFailureCallback(new WLWifiFailureCallback() {
     @Override
     public void execute(WLWifiError wifiError) {
        onWifiFailure(wifiError);
     }
  });
  WLClient.getInstance().getWLDevice().startAcquisition(new WLLocationServicesConfiguration()
    .setPolicy(policy)
    .setTriggers(triggers)
    .setFailureCallbacks(Collections.singletonList(failures)));
  void stopScanning() {
    WLClient.getInstance().getWLDevice().stopAcquisition();
  void onWifiFailure(WLWifiError wifiError) {
    // Show an error message to the user...
  }               
  // Receives a string, indicating the name of the region
  void setCurrentRegion(String region) {
    WLProcedureInvocationData invocData = new WLProcedureInvocationData ("HT_WifiScan", "setAppContext");
    invocData.setParameters(new Object[] {"{regionName: '" + region + "'}"});
  WLClient.getInstance().invokeProcedure(
    invocData, new WLResponseListener() {
      @Override
      public void onSuccess(WLResponse response) {
        // update UI, indicating success
      @Override
      public void onFailure(WLFailResponse response) {
        // update UI, indicating error
    }
  );
}

Application to set up acquisition policy, including triggers - native iOS

// NSMutableSet* ssids  -- is defined in the header as a member field and initialized as ssids = [NSMutableSet set];
    
  -(void) addNetworkToBeScanned: (NSString*) ssid {
     [ssids addObject:ssid];
  -(void) removeNetwork: (NSString*) ssid {
     [ssids removeObject:ssid];
 -(void) startScanning {
     NSMutableArray* filters = [[NSMutableArray alloc] init];
 
     for (NSString* ssid in ssids) {
       [filters addObject: [[WLWifiAccessPointFilter alloc] initWithSSID:ssid MAC:WILDCARD]];
    }
 
     WLAcquisitionPolicy* policy = [
       [[WLAcquisitionPolicy alloc] init]
       setWifiPolicy: [[
         [[WLWifiAcquisitionPolicy alloc] init]
         setInterval: 3000]
         setAccessPointFilters: filters]
     ];
         
     WLTriggersConfiguration* triggers = [[WLTriggersConfiguration alloc] init];
     [[triggers getWifiTriggers]
       setObject: [
         [[WLWifiVisibleAccessPointsChangeTrigger alloc] init]
         setEvent: [NSDictionary dictionaryWithObject: @"moved" forKey:@"name"]]
       forKey:@"change"
     ];
     WLAcquisitionFailureCallbacksConfiguration* failures = [[WLAcquisitionFailureCallbacksConfiguration alloc] init];
     [failures setWifiFailureCallback: [WLCallbackFactory createWifiFailureCallback:^(WLWifiError* wifiError) {
       [self onWifiError: wifiError];
     }]];
     [[[WLClient sharedInstance] getWLDevice] startAcqusition:
       [[[
         [[WLLocationServicesConfiguration alloc] init]
         setPolicy: policy]
       setTriggers: triggers]
       setFailureCallbacks: [NSMutableArray arrayWithObject:failures]]];
  -(void) stopScanning {
    [[[WLClient sharedInstance] getWLDevice] stopAcqusition];
         
  -(void) onWifiFailure: (WLWifiError*) wifiError {
    //show an error message to the user...
         
  // receives a string, indicating the name of the region
  -(void) setCurrentRegion: (NSString*) region {
    WLProcedureInvocationData* invocData = [[WLProcedureInvocationData alloc] initWithAdapter:@"HT_WifiScan"  procedure:@"setAppContext"];
    [invocData setParameters:[NSArray arrayWithObject:[NSString stringWithFormat:@"{regionName: '%@'}", region]]];
    // Replace this code with a WLDelegate instance that will update the UI indicating success/failure.
    id<WLDelegate> delegate = nil;
    {[WLClient sharedInstance] invokeProcedure:invocData withDelegate: delegate];
  }

Adapter logic to update application context and handle events

// defined as a procedure:
function setAppContext(context) {
  WL.Server.setApplicationContext(JSON.parse(context));
}
function handleEvent(event) {
  // Nothing specific to do, the event device context will be logged to raw reports database in any case.
}
// log all events
WL.Server.setEventHandlers([WL.Server.createEventHandler({}, handleEvent)]);

Example of Enter trigger - hybrid Android, iOS, and Windows Phone 8

var triggers = {
  Wifi: {
    welcomeToMall: {
      type: "Enter",   areaAccessPoints: [{SSID: "FreeMallWifi"}]
      callback: showWelcome
    }
    foodCourt: {
      type: "Enter",   areaAccessPoints: [{SSID: "FreeMallWifi", MAC: "12:34:56:78:9A:BC"}, {SSID: "FreeMallWifi", MAC: "CB:A9:87:65:43:21"}]
      callback: showFoodCoupons
    }
};

Example of Enter trigger - native Android

WLTriggersConfiguration triggers = new WLTriggersConfiguration();
triggers.getWifiTriggers().put(
  "welcomeToMall",   new WLWifiEnterTrigger()
    .setAreaAccessPoints(Collections.singletonList(new WLWifiAccessPointFilter("FreeMallWifi")))
      .SetCallback(showWelcome));
triggers.getWifiTriggers().put(
  "foodCourt",   new WLWifiEnterTrigger().setAreaAccessPoints(Arrays.asList(
    new WLWifiAccessPointFilter("FreeMallWifi", "12:34:56:78:9A:BC"), new WLWifiAccessPointFilter("FreeMallWifi", "CB:A9:87:65:43:21"))).SetCallback(showFoodCoupons));

Example of Enter trigger - native iOS

WLTriggersConfiguration triggers = new WLTriggersConfiguration();
        
WLTriggersConfiguration* triggers = [[WLTriggersConfiguration alloc] init];
[[triggers getWifiTriggers] setObject:
   [[
     [[WLWifiEnterTrigger alloc] init]
       setAreaAccessPoints: [NSMutableArray arrayWithObject: [[WLWifiAccessPointFilter alloc] init: @"FreeMallWifi"]]]
       setCallback: showWelcome]
   forKey:@"welcomeToMall"];
[[triggers getWifiTriggers] setObject:
  [[
    [[WLWifiEnterTrigger alloc] init]
      setAreaAccessPoints: [NSMutableArray arrayWithObjects:
        [[WLWifiAccessPointFilter alloc] initWithSSID: @"FreeMallWifi" MAC: @"12:34:56:78:9A:BC"],     [[WLWifiAccessPointFilter alloc] initWithSSID: @"FreeMallWifi" MAC: @"CB:A9:87:65:43:21"],     nil]]
      setCallback: showFoodCoupons]
   forKey:@"foodCourt"];


Parent topic: Location services