Unofficial.Owlet 0.1.4

dotnet add package Unofficial.Owlet --version 0.1.4                
NuGet\Install-Package Unofficial.Owlet -Version 0.1.4                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Unofficial.Owlet" Version="0.1.4" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Unofficial.Owlet --version 0.1.4                
#r "nuget: Unofficial.Owlet, 0.1.4"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Unofficial.Owlet as a Cake Addin
#addin nuget:?package=Unofficial.Owlet&version=0.1.4

// Install Unofficial.Owlet as a Cake Tool
#tool nuget:?package=Unofficial.Owlet&version=0.1.4                

Owlet Smart Sock API for .NET

Unofficial .NET Standard 2.0 Library. Run on Linux, Mac, Windows.

Build status nuget.org Release

Notice

This code is in no way affiliated with, authorized, maintained, sponsored or endorsed by Owlet Baby Care or any of its affiliates or subsidiaries. This is an independent and unofficial API. Use at your own risk.

Example Data

Device Properties for specific Owlet Smart Sock:

[
  {
    "Name": "BATT_LEVEL",
    "Value": 93,
    "DataUpdatedAt": "2018-01-01T12:34:56Z"
    // ...
  },
  {
    "Name": "HEART_RATE",
    "Value": 125,
    "DataUpdatedAt": "2018-01-01T12:34:56Z"
    // ...
  },
  {
    "Name": "OXYGEN_LEVEL",
    "Value": 99,
    "DataUpdatedAt": "2018-01-01T12:34:56Z"
    // ...
  }
  // ...
]

Using this Owlet API Wrapper

Install NuGet package

dotnet add package Unofficial.Owlet

Using .NET Core Dependency Injection

// ...
using Unofficial.Owlet.Extensions;

// ...

private void ConfigureServices(IServiceCollection services)
{
    // ...

    services.AddOwletApi();

    // ...
}

Then, you can import the specific API services you would like to consume into your class:

namespace My.App
{
    public class MyClass
    {
        private readonly IOwletUserApi _owletUserApi;
        private readonly IOwletDeviceApi _owletDeviceApi;
        public MyClass(IOwletUserApi owletUserApi, IOwletDeviceApi owletDeviceApi)
        {
            this._owletUserApi = owletUserApi;
            this._owletDeviceApi = owletDeviceApi;
        }

        public async Task Run()
        {
            var email = ""; // from IOptions<YourConfigClass> or input
            var password = ""; // from IOptions<YourConfigClass> or input
            await Login(email, password);

            var mySock = await GetDeviceSerialNumber(); // get first sock in your account
            var mySockProperties = await GetDeviceProperties(mySock.Device.DeviceSerialNumber);

            foreach (var property in mySockProperties)
            {
                Console.WriteLine($"{property.Property.Name} : {property.Property.Value} ({property.Property.DataUpdatedAt?.ToString("MM/dd/yyyy HH:mm")})");
            }
        }

        public async Task<OwletSignInResponse> Login(string email, string password)
        {
            return await this._owletUserApi.LoginAsync(email, password);
        }

        public async Task<string> GetDeviceSerialNumber()
        {
            return await this._owletDeviceApi.GetDevicesAsync()
                .First()?.Device?.DeviceSerialNumber;
        }

        public async Task<IEnumerable<OwletDevicePropertyResponse>> GetDeviceProperties(string deviceSerialNumber)
        {
            return await this._owletDeviceApi.TryGetFreshDevicePropertiesAsync(deviceSerialNumber);
        }
    }
}

Setting Up Dependency Injection with User Settings

You may want to configure your account email/password in your startup method, then forget about it later. Or you may want to always use your current sock's device serial number (if you know it - or find it using this library), so that you can reduce an additional API call to speed up retrieval of your Owlet Smart Sock data.

using Unofficial.Owlet.Extensions;
using Unofficial.Owlet.Models;

// ...

private void ConfigureServices(IServiceCollection services)
{
    services.AddOwletApi(options =>
    {
        options.WithSettings(new OwletApiSettings
        {
            Email = "abc@abc.com",
            Password = "mypassword",
            KnownDeviceSerialNumbers = new [] { "A...." }
        });
    });

    // ...
}

Then, if no accessToken is provided to some of the API methods, it will check for this configuration value and log you in as needed automatically. No more calls to LoginAsync(email, password)!

namespace My.App
{
    public class MyClass
    {
        // ...

        public async Task Run()
        {
            // no need to make explicit LoginAsync() call!
            // this Owlet API library will try for you!

            var mySock = await GetDeviceSerialNumber(); // get first sock in your account
            var mySockProperties = await GetDeviceProperties(mySock.Device.DeviceSerialNumber);

            foreach (var property in mySockProperties)
            {
                Console.WriteLine($"{property.Property.Name} : {property.Property.Value} ({property.Property.DataUpdatedAt?.ToString("MM/dd/yyyy HH:mm")})");
            }
        }

        // ...
    }
}

Managing User Secrets

This is not required to consume this library, but a note nonetheless.

If you use the IOptions<T> pattern to read configuration from appsettings.json, you may want to consider storing your Email/Password in a secrets.json file stored on your client machine outside of this (or your own) code repository - which can accidentally leak to the public!

# recommend .NET Core 2.1+ SDK

dotnet tool install -g dotnet-user-secrets

cd $ProjectRoot
cd src/Unofficial.Owlet.TestConsole/
dotnet-user-secrets list
dotnet-user-secrets set owlet:email abc@abc.com
dotnet-user-secrets set owlet:password mypassword

How Does It Work?

I touched on this briefly above, but it's worth calling out again.

Other collaborators have identified that it's easy enough to request device properties from the Ayla Networks IoT platform, but the data ends up getting stale after ~30 seconds, or after the Owlet app has closed.

As it turns out, there's a property returned named APP_ACTIVE that reads as 1 when the mobile application is in-use, and 0 when not.

If we want to fake that the app is always open so that we can collect data over a time period, we need to continually write a new APP_ACTIVE datapoint with value of 1 such that the Owlet base station will broadcast the other assorted sock datapoints. Of course, there is a time delay between the initial mark of APP_ACTIVE to when the base station is able to create new datapoint records. So, this library takes a swing at 2 seconds ... even though realistically, that's likely not sufficient for most experiences (2 seconds between notifying Owlet API that it's active, having data requested from the base station, and the base station replying with datapoints).

I mentioned the risks above earlier, too. You can see that this library is making the assumption that there is a property named APP_ACTIVE and that its value needs to be 1 in order for data to update. If this property ever gets renamed, or the value it needs to be is ever updated, or an alternate mechanism is introduced, this library will need to be updated for that.

Data Flow

  1. Login (/users/sign_in)
  2. Get Devices for Account (/apiv1/devices)
  3. Get Device Properties (/apiv1/dsns/{deviceSerialNumber}/properties)
    • Are properties stale?
      1. Create new datapoint marking app as "active" (/apiv1/properties/{propertyId}/datapoints)
      2. Wait a few seconds for data to propagate
      3. Query for Device Properties again (repeat)

2018 Mitchell Barry

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.1.4 1,034 7/8/2018
0.1.4-CI-20180722-132452 677 7/23/2018
0.1.4-CI-20180708-114345 780 7/8/2018
0.1.3 901 7/8/2018
0.1.3-CI-20180708-112047 783 7/8/2018
0.1.2 933 7/8/2018
0.1.2-CI-20180708-104426 784 7/8/2018
0.1.1-CI-20180708-101247 760 7/8/2018
0.1.0-CI-20180708-083220 768 7/8/2018