Beacon Bulk Updater

<!doctype html>

Beacon Bulk Updater

We’re always looking for solutions to make it easier to manage bulk quantities of beacons. And let’s face it, the easiest way to do something is when it happens all by itself. And that’s exactly what the Bulk Updater is for. The aim of this function is simple: to let you apply updates to your beacons without the need to connect to them individually. In short, once you have the BulkUpdater function from our SDK included in your app, all you need to do is start it, and it will automatically detect all of your beacons in range of the device it’s running on, and sync all settings changes with your Estimote Cloud. It takes just 5 minutes, on average, to update 40 beacons — that’s just a few seconds for one beacon.

And to top it off, it can even update the beacons’ firmware for you!

What does it do in practical terms? Let’s see:

  • you can very quickly apply any settings changes to your beacon dev kit;
  • you can update your entire indoor location infrastructure with new settings pretty much just walking around and admiring the scenery—since you don’t even need to look at your phone;
  • and you can pre-configure your bulkbag of beacons in mere minutes, significantly cutting down the preparation time for deployment.

What’s ahead (aka Table of Contents)

Prerequisites

  • 1 x Mac with Xcode OR 1 x computer with Android Studio.
  • 1 x iPhone 4S or newer OR 1 x device with Android 4.3 or newer with BLE support.
  • 1 x Estimote Account (sign up here).
  • 2 or more Estimote Beacons (hardware revision G1.8, F2.3, or later) assigned to your account.

iOS: Create your Xcode project and add the Estimote SDK

Start with either your existing app, or with a new, blank project in Xcode (to keep things simple, let’s call it Bulkr). Once the project is ready, be sure to import the Estimote SDK. To do that:

  • Download the Github iOS repo
  • Create a new group named Estimote in your project
  • Drag the EstimoteSDK.framework file into the Estimote group.

![image](upload://ib80Ps6pFTKRejzpHHxC4uZnlMH.png)dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:23.0.1'

// add the following line, and replace "1.0.1" with the latest version
// of Estimote Android SDK; you'll find the latest version number on:
//   https://github.com/Estimote/Android-SDK/releases
compile 'com.estimote:sdk:1.0.1@aar'

}

Android Studio will now show a “Gradle files have changed since last project sync …” warning. Just click “Sync Now” and the Estimote SDK will be automatically added to the project.

Runtime permissions (Android 6.0 or later)

There’s one extra step you need to perform if your app’s targetSdkVersion is 23 (i.e., Android 6.0) or greater—and that’s to request the ACCESS_COARSE_LOCATION permission at runtime. You can read more in Google’s Requesting Permissions at Run Time article, or in the Android 6.0 and runtime permissions section of our own Estimote Android SDK readme.

We can’t automatically request these permissions for you, like we do with the manifest file, but we do provide helper methods to make it really easy. One of these methods is SystemRequirementsChecker’s checkWithDefaultDialogs. It goes through a predefined checklist and makes sure everything is accounted for, including the runtime permissions (and also things like, is Bluetooth on, is Location on, etc.)

Let’s call it in the MainActivity:

@Override
protected void onResume() {
super.onResume();
SystemRequirementsChecker.checkWithDefaultDialogs(this);
}

Info: The checkWithDefaultDialogs will use default system dialogs if it needs to ask the user for anything—e.g., to turn Bluetooth or Location on, or … yes, ask for ACCESS_COARSE_LOCATION permission.

This is great for this tutorial, as we want to get up and running quickly—but in a production app, you might want to design your own UI, or even an entire onboarding process, to explain why your app requires location and Bluetooth, and what’s in it for the user.

The check method might be a better option then. It won’t trigger any dialogs, but will simply report anything that’s missing to a callback—leaving how to handle it up to you.

Configure your beacons

Adjust the settings of your beacons through the Estimote Cloud. Remember to “Save” them once you’re done! These will create pending settings for your beacon. These need to be applied to the beacon. If not for the BulkUpdater, you’d need to connect to each beacon manually.

// Declare Bulk Updater as a property
let bulkUpdater = ESTLocationBeaconBulkUpdater()

// Set it up and start it in your viewDidLoad like this:
bulkUpdater.delegate = self
self.bulkUpdater.start()

// Declare Bulk Updater as a property
@property (nonatomic, strong) ESTLocationBeaconBulkUpdater *bulkUpdater;

// Set it up and start it in your viewDidLoad like this:
self.bulkUpdater = [ESTLocationBeaconBulkUpdater new];
self.bulkUpdater.delegate = self;
[self.bulkUpdater start];

This will make the Bulk Updater run a bit like the Duracell bunny—it will keep going, and going, and going… until you explicitly cancel it. To avoid that, you might want to start it with timeout—this lets you specify the time after which the Bulk Updater will stop looking for beacons to update. Addtionally, you can also set the fetchInterval, i.e. specify the time between the Bulk Updater’s checking for new pending settings in your Esitmote Cloud account.

Android

The Android implementation follows the same approach. You’ll be able to fetch all the settings that are pending in your account, and quickly apply them to your beacons. To begin with, build the BulkUpdater object as follows:

BulkUpdater bulkUpdater = new BulkUpdaterBuilder(this)
.withFirmwareUpdate()
.withCloudFetchInterval(5, TimeUnit.SECONDS)
.withTimeout(0)
.withRetryCount(3)
.build()

For a detailed description of the parameters, check out our repo’s docs.

You can then start listening for beacons (a.k.a ConfigurableDevices), and have your code react to each device status change. You can pass a listener while starting the BulkUpdater, and we recommend starting it in your Activity’s onResume() method.

@Override
protected void onResume() {
super.onResume();

bulkUpdater.start(new BulkUpdater.BulkUpdaterCallback() {
    @Override
    public void onDeviceStatusChange(ConfigurableDevice device,
            BulkUpdater.Status newStatus, String message) {
        Log.d("BulkUpdater", device.deviceId + ": " + newStatus);
    }

    @Override
    public void onFinished(int updatedCount, int failedCount) {
        Log.d("BulkUpdater", "Finished. Updated: " +
                updatedCount + " Failed: " + failedCount );
    }

    @Override
    public void onError(DeviceConnectionException e) {
        Log.d("BulkUpdater", "Error: " + e.getMessage());
    }
});

}

You can also stop bulk updater whenever you want. Just use the stop() method:

bulkUpdater.stop();

Don’t forget : Because BulkUpdater uses an underlying service for connecting to devices, so it is necessary to call destroy() in your activity’s onDestroy method. This will prevent any memory leaks.

@Override
protected void onDestroy() {
super.onPause();
bulkUpdater.destroy();
}

Manual bulk operations

iOS

You might be wondering why we provide two modes, since the automatic one seems to cover most needs. Well, the second mode is for those of us who want greater control over the specific settings that are applied. Let’s say there are certain settings you’ve configured in your Cloud account that you don’t want applied just yet—the manual mode lets you skip them. With this mode, you can also choose the specific beacons you want updated. This works great for testing, for instance.

Additonally, with manual operation you have greater visiblility into the progress of the update process.

And finally, you can select the interval at which new settings are read from the cloud. This is what we refer to as the fetch interval, and its function is just that—to define how frequently the BulkUpdater function checks for new settings to be applied.

In short, this mode is the one for you, if you’re looking for more granular control and visibility, as opposed to the automatic mode, which is more fire-and-forget (just don’t actually forget that it’s running!).

Now, let’s imagine you’d like to set the value of Major for all your beacons to 102. This is how you’d go about doing that:

Swift Objective-C
func customConfigurations() -> [ESTLocationBeaconBulkUpdateConfiguration] {
let beaconIdentifiers = [“02bd06ced9e57f8945b881c11e6d5518”,
“833bad3133274abaa47cfd5c59b1432e”]

// Prepare configurations for each of them
var configurations = [ESTLocationBeaconBulkUpdateConfiguration]()

for identifier in beaconIdentifiers {
    // Prepare an operation for changing major to 102
    let major: ushort = 102;
    let majorSetting = ESTSettingIBeaconMajor(value: major)!
    let majorOperation = ESTBeaconOperationIBeaconMajor.writeOperation(
            withSetting: majorSetting, completion: { (setting, error) in
        // handle the completion here
    })

    let configuration = ESTLocationBeaconBulkUpdateConfiguration(
        deviceIdentifier: identifier,
        settingsOperations: [enableOperation, majorOperation],
        firmwareUpdateAvailable: false)
    configurations.append(configuration)
}

return configurations

}

- (NSArray<ESTLocationBeaconBulkUpdateConfiguration *> *)customConfigurations {
NSArray *beaconIdentifiers = @[@“833bad3133274abaa47cfd5c59b1432e”,
@“02bd06ced9e57f8945b881c11e6d5518”];

// Prepare configurations for each of them
NSMutableArray&lt;ESTLocationBeaconBulkUpdateConfiguration *&gt; *configurations =
    [NSMutableArray new];

for (NSString *identifier in beaconIdentifiers) {
    // Prepare an operation for changing major to 102
    unsigned short major = 102;
    ESTSettingIBeaconMajor *majorSetting =
        [[ESTSettingIBeaconMajor alloc] initWithValue:major];
    ESTBeaconOperationIBeaconMajor *majorOperation =
        [ESTBeaconOperationIBeaconMajor
         writeOperationWithSetting:majorSetting
         completion:^(ESTSettingIBeaconMajor * _Nullable major,
                      NSError * _Nullable error) {
             // handle the completion here
    }];

    ESTLocationBeaconBulkUpdateConfiguration *configuration =
        [[ESTLocationBeaconBulkUpdateConfiguration alloc]
         initWithDeviceIdentifier:identifier
         settingsOperations:@[enableOperation, majorOperation]
         firmwareUpdateAvailable:false];
    [configurations addObject:configuration];
}

return [NSArray arrayWithArray:configurations];

And finally, to initialize the BulkUpdater, include

Swift Objective-C
self.bulkUpdater.start(with: self.customConfigurations())
[self.bulkUpdater startWithUpdateConfigurations:[self customConfigurations]];

Android

You can get similar results on Android, just incorporate POST calls to our API for the specific devices you want to focus on.

In other words:

  • determine which devices you want to update
  • prepare the relevant settings and format them as a JSON
  • send them to the Estimote Cloud
  • run BulkUpdater, which will fetch the new configuration and apply it to the relevant beacons.

This mode—manual bulk updates—is what we’re using in our Deployment App for iOS.