Firefly Design LLC

Product Design Consulting

Firefly Ice: Getting Started: Using the API

The Firefly Ice API is used by apps to communicate with the Firefly Ice device.  Apps can communicate wirelessly via Bluetooth 4.0 Low Energy and/or USB.

The first implementation of the Firefly Ice API is for iOS & Mac OS X.  This document covers that API.

Windows and Android support are planned for future releases.  These libraries will be conceptually similar, but will be implemented using native code on their respective platforms.

Quick Start

The API has a quick way to get started.  This can be used to start working with the Firefly Ice device without needing in depth knowledge of the device or the Core Bluetooth Framework.

The quick start approach is to simply create a FDFireflyIceManager object when your app starts.  The manager will notify your delegate when a device is discovered.  You can then connect/disconnect to devices as desired.

If you want to dig deeper then a good place to start is by looking at the FDFireflyIceManager source code.

Example: FireflyGame

The FireflyGame example is a complete project that is setup to discover, connect, and sync activity data from a Firefly Ice device.

Grab the source code from Github, open the FireflyGame Xcode project for iOS or Mac OS X, and run.

git clone https://github.com/denisbohm/firefly-ice-api.git
open firefly-ice-api/MacOSX/FireflyGame/FireflyGame.xcodeproj
open firefly-ice-api/iOS/FireflyGame/FireflyGame.xcodeproj

The first screen shows nearby Firefly Ice devices that have been discovered.

Touch one of the devices to move to the next screen and connect to the device.  The next screen has a 30 second timer.  Touch "Play!" to start the timer.  During the 30 seconds move your Firefly Ice.

After the 30 seconds is up, the activity data is synced from the device.

When the sync is complete the activity "points" are shown.

You can play again or go back to the previous screen to pick a different device.

 Source Code: Dive In

The FDViewController class is where most of the interesting interaction with the FDFireflyIceManager and the app happens.

When the FDFireflyIceManager is initialized the delegate is set to the FDViewController and the Firefly Ice manager is made active.

    _fireflyIceManager = [FDFireflyIceManager manager];
    _fireflyIceManager.delegate = self;
    _fireflyIceManager.active = YES;

When the discovery screen (FDPickScene) is shown then the Firefly Ice manager discovery is enabled.

- (void)showPickScene
...
    _fireflyIceManager.discovery = YES;

When a device is discovered the delegate is called and the discovery screen is updated.

- (void)fireflyIceManager:(FDFireflyIceManager *)manager discovered:(FDFireflyIce *)fireflyIce
{
    if (![_fireflyIces containsObject:fireflyIce]) {
        [_fireflyIces addObject:fireflyIce];
    }
    [_pickScene updateDevices:_fireflyIces];
}

When a device is touched the screen transitions to the play screen. Discovery is turned off.

- (void)pickSceneChoose:(FDPickScene *)pickScene fireflyIce:(FDFireflyIce *)fireflyIce
{
...
    [self showPlayScene:fireflyIce];
    
    id channel = fireflyIce.channels[@"BLE"];
    [channel open];
}

- (void)showPlayScene:(FDFireflyIce *)fireflyIce
{
    _fireflyIceManager.discovery = NO;
...
}
When the timer counts down to zero an activity data sync is started.
- (void)playSceneSync:(FDPlayScene *)playScene
{
    FDFireflyIce *fireflyIce = _playScene.fireflyIce;
    FDFireflyIceChannelBLE *channel = fireflyIce.channels[@"BLE"];
...
    FDSyncTask *syncTask = [FDSyncTask syncTask:identifier fireflyIce:fireflyIce channel:channel delegate:self identifier:_syncIdentifier];
    [fireflyIce.executor execute:syncTask];

Various sync task delegate methods are called during the sync.

- (void)syncTask:(FDSyncTask *)syncTask site:(NSString *)site hardwareId:(NSString *)hardwareId time:(NSTimeInterval)time interval:(NSTimeInterval)interval vmas:(NSArray *)vmas backlog:(NSUInteger)backlog
{
    for (NSNumber *vma in vmas) {
        ++_syncVmaCount;
        _syncVmaTotal += vma.doubleValue;
    }
}

- (void)syncTask:(FDSyncTask *)syncTask progress:(float)progress
{
    FDFireflyDeviceLogInfo(@"sync progress (hardwareId=%@ identifier=%@)", syncTask.hardwareId, syncTask.identifier);
    if ([syncTask.identifier isEqualToString:_syncIdentifier]) {
        [_playScene syncProgress:progress];
    }
}

- (void)syncTaskComplete:(FDSyncTask *)syncTask
{
    FDFireflyDeviceLogInfo(@"sync complete (hardwareId=%@ identifier=%@ lastDataDate=%@)", syncTask.hardwareId, syncTask.identifier, syncTask.lastDataDate);
    if ([syncTask.identifier isEqualToString:_syncIdentifier]) {
        NSUInteger points = 0;
        if (_syncVmaCount > 0) {
            points = (NSUInteger)(10.0 * _syncVmaTotal / _syncVmaCount);
        }
        [_playScene syncComplete:points];
        _syncIdentifier = nil;
    }
}
X