NAV Navbar
shell

Octave Documentation

Octave is a distributed data orchestration platform from Sierra Wireless. Octave makes it easy to securely extract, orchestrate, and act on data from your assets at the edge to the cloud. You’re in the right place to learn more about Octave, what you can do with it, and how to get started.

We’ve opened a preview of Octave for OEMs and Enterprises looking to accelerate the development of their asset monitoring solutions. If you like what you see here and want to get started, go to sierrawireless.com/octave and apply to access the preview.

Getting Started with Octave

Introduction

Thank you for choosing Octave, the data orchestration platform of Sierra Wireless.

Before you start, you shall have received:

Starter Kit

Beta Starter Kit

WP7702 Module

The Sierra Wireless AirPrime WP7702 offers LTE-M connectivity with 2G fallback for any cellular network. The module provides both the Low-Power Wide-Area cellular communication capability as well as a host processor for the Octave Edge Package.

Octave Edge Package

The Octave Edge Package defines a tightly-coupled set of software components which provide out-of-the-box high level services for IoT Data Orchestration. The individual components consist of:

Sierra Wireless Connectivity

The cellular module comes with a Sierra Wireless multi-operator SIM delivering multi-operator coverage, superior data service quality, and resilience to outages -- whether your application is global, regional, or local.

MangOH Red Development Board

The mangOH Red provides an out-of-the box hardware reference design and enables rapid prototyping of new ideas. The board provides sensors: light, pressure and temperature, gyroscope, accelerometer as well as a Raspberry Pi connector and a slot for an industrial IoT expansion card. Once your prototype is complete, you can then adapt and reuse the industrial-grade design as well as Sierra Wireless AirPrime modules in final production.

The mangOH red Octave comes complete with a Raspberry Pi compatible 4-line LCD as well as cellular and GPS antennas. The antennas are required in order to achieve strong cellular and satellite signals.

The LCD is used to provide on-board feedback and serve as a debugging output.

The Octave kit also includes an USB cable used to power your mangOH red from either a laptop or an external power supply.

Powering Up

Power up your mangOH by connecting one of the USB cables to a power source:

mangoh red connected to usb power

It takes approximately 30 seconds for the mangOH to boot. At the end of the sequence you will see a welcome message on the first line of the LCD.

The welcome message on the LCD indicates that your device is ready. Now you can now login into the platform.

Logging In

Each Octave user has an account created on Sierra Wireless data orchestration platform.

You should have received an email from Sierra Wireless team with your username and password. If you are having difficulty use the chat button on the lower right of this page to contact the Support team.

Navigate to https://octave.sierrawireless.io to login:

login window

You are directed to a page showing the details of your device in the platform.

Verifying Connectivity to Cloud

Your device is configured in Developer Mode, that means that it is always connected to the platform so that any change in the cloud will be applied on the device within few seconds.

Before we go further, check the last seen date on the device details page of the web UI as well as developer mode status.

If you have trouble connecting your device, you can double check that the LCD is still showing the welcome message, the cellular antenna is correctly plugged in, and you are in an area with some cellular coverage. In case all these are good then do not hesitate to contact Octave support.

Cloud Navigation Introduction

Octave's main navigation menu is on left part of the screen:

Left Navigation Screenshot

Device context

This section of the navigation is for working within the frame of reference of a single device, be it for prototyping, configuring, or debugging. When in the device context, the device picker at the top of the screen allows you to chose which device you'd like to work with.

Cloud context

This section of the navigation is used to access the tools used to visualize and orchestrate streams of data flowing between your devices, the cloud platform, and external applications or cloud services. The most common use for Octave developers will be to use Cloud Actions to send data to external services or applications via WebHooks.

Deployment context

This section of the navigation provides a global view of the many pieces of configuration and business logic that -- when assembled and applied to your fleet -- define the processing and data flow of your solution.

Commanding Resources

Lets start working with Octave by setting the value of the /lcd/txt1 in the Resources window. This change will be propagated to the device over the cellular network. The Cloud Interface takes this change, sends it to the Data Hub, where the LCD driver will read the update and display the new value on the screen. Follow along below:

Locate the LCD Resource and Select Configure

Select LCD Configure

Set Text and "Configure"

Set and Save LCD Configuration

Confirm LCD Displays the new Text

Set and Save LCD Configuration

Configuring Sensor Resources

Some resources require configuration before they can be used. The light sensor on a mangoOH red is one such resource. We need to tell the sensor driver how often we'd like it to report data to the Data Hub.

Here is where you can find the light sensor on your mangOH

MangOH Red Light Sensor

Select Configure

Select Light Resource

Select Light Resource

Set Polling Period to 5 Seconds

Set Light Polling

Check Resource in the cloud has Updated

After a few seconds you'll see that the "Last Value" for the light sensor in the cloud has updated. You see this update on the Resources screen because the device is in Developer Mode. Remember, in Developer Mode the device periodically updates all of it's last-known values for every Resources, regardless if it is set to be Observed or not.

Light Resource Value Updated

Sending Data Directly to the Cloud

In order to send data to the cloud when we are not in Developer Mode, we'll need to create an Observation.

Select "Create New Observations" from the Resources screen

Beta direct 1

Beta direct 2

Name the observation "light_direct"

Beta direct 3

Send it to the "Cloud Immediately"

Beta direct 4

View it in the Observations List

Beta direct 5

Check for a newly-created Stream named "light_direct"

This stream is automatically created in the cloud using the same name as the observation that exists on the device.

Beta direct 6

Inspect the Events in the stream

When new events arrive in the cloud from the device we store them in a Stream and add additional meta-data. Note that both the time at the edge, as well as the time received in the cloud are stored. Having both of these times available for each event is important when we try more sophisticated data orchestration strategies.

Beta direct 7

Sending Data Periodically

While some data benefits from being sent to the cloud instantly, there is a cost for choosing to do so. Firstly, every time we start and stop the cellular data session, power is consumed. Secondly, encapsulating each reading for transmission individually causes more data to be sent over the air. This is reflected in pricing.

In order to optimize our power and bandwidth consumption, we can chose to have our readings stored and forwarded on a periodic basis. The Cloud Interface provides us with a buffer for Events we wish to store and allows us to dynamically configure how often that buffer is sent to the cloud.

It is important to note that you are configuring the maximum time between transmissions. For example, if the store and forward buffer is set for 60 seconds and an "immediate" event is generated 30 seconds after the last transmission, the contents of the buffer will be sent with the "immediate" event, and the 60 second timer reset.

The /cloudInterface/store_forward resource controls that maximum time between transmissions.

Configure store and forward for 1 Minute

Beta periodic 1

Beta periodic 2

Edit our light_direct Observation

We can edit our light_direct observation to now use store and forward.

Beta periodic 4

Beta periodic 5

Note the changed "Send To"

Beta periodic 6

Inspect the Events after waiting for about a minute

Note the difference in "Generated" dates and "Received" dates.

Beta periodic 7

Beta periodic 8

Pre-Processing Raw Data with a Buffer and Mean

Now that we know how to store and forward readings, we may look at our data in the cloud and realize that we really don't need EVERY reading. Perhaps we only need the average reading for the last minute. We can set our observation to store a "buffer" of the last 12 readings and only report the mean value of the readings every minute.

Create or Edit an Observation

Beta mean 1

Set to buffer to 12 readings and Throttle to 60 seconds

Beta mean 2

Send to cloud immediately

Beta mean 3

Beta mean 4

Observe the Mean Readings in the Cloud

Beta mean 5

Beta mean 6

Experiment with Observations

Now that you know the basics, please experiment with all of the possible combinations of rules in Observations.

Forward data to AWS

As we have seen in the previous step, device events are organized into entities called "streams" represented by a single path (example: /bk_beta/devices/beta-device/light_store_forward). As a user you can create your own streams via the Web UI and the API.

Streams can be processed on Octave platform by Cloud Actions. A Cloud action is a script written in Javascript language and is executed on Events entering a Stream in the cloud. The Cloud Action takes an Event as input and can produce zero or more events in any pre-defined stream while also accessing data from other Streams and executing REST requests.

The following Cloud Action takes the events coming from the light sensors, adds the device name and timestamp, and forwards the event via a WebHook to a REST endpoint running on Amazon Web Services.

Create New Cloud Action

Beta aws 1

Select Source, Name, Enter Javascript

Beta aws 2

  1. Select the input Stream for the Cloud Action
  2. Enter a name for your Cloud Action
  3. Enter the Javascript function that will process incoming Events in the Stream. The Cloud Action will be executed for each new Event created in the input Stream
  4. Commit your changes when done and the Cloud Action will be deployed and begin processing

Example AWS Cloud Action


// Example of Cloud action forwarding events to AWS via API gateway
function(input){
  var deviceId = input.path.split("/")[3];
  var payload = {"deviceId": deviceId, "light": input.elems.redSensor.light, "creationDate": input.generatedDate};
  var postBody = JSON.stringify(payload);
  var postHeaders = {'Content-Type':'application/json'};
  // update the url with your own service, this one is a simple MOCK returning always OK
  var result = Octave.Http.post('https://m6d92mu3a2.execute-api.eu-west-1.amazonaws.com/bk_beta', postHeaders, postBody);
  var new_event = {"elems": result };
  return {"/bk_beta/users/ui/:inbox": [new_event]};

}

See example code to the right.

View Results in your :inbox stream

You can post results to the :inbox stream in your personal namespace.

Beta aws 2

Setting a Resource on a device from a Cloud Action

There are two ways to set a devices Resource value from the cloud. The first is to modify the state of the Device Object. Changes in the device object are persisted through restart. The second is to write an Event to the :command stream in the device's namespace. These changes are pushed to the device but are not written to device storage (so the value will revert on restart). For this (somewhat contrived) use case, that is our desired behavior.

We'll be taking the light reading from the device, processing it in the cloud, and then setting the /lcd/txt1 resource via the command stream.

function (input) {
   var result = {};
   var threshold = 1500;

   if (input.elems.redSensor.light > threshold) {
     result["/bk_beta/devices/beta-device/:command"] = [{ elems : { lcd : { txt1 : "Light threshold!" } }}];
   }
  else {
    result["/bk_beta/devices/beta-device/:command"] = [{ elems : { lcd : { txt1 : "Light:"+ input.elems.redSensor.light  } }}];
  }
   return result;
}

First we create a cloud action to read our light readings.

Beta command 1

To the right is some code you can copy/paste.

Setting a Resource from an Observation

If you only need to forward sensor data to an actuator, there is no need to go to the cloud. We can forward Events created by Observations directly to other resources (like the LCD) from within an Observation itself.

First, create an observation with the destination as another Resource.

Beta o2r 1 Beta o2r 2 Beta o2r 3 Beta o2r 4

You'll be able to see the light value is now displayed on the LCD.

Beta o2r 5

Processing Edge Data

Going one step further, we can use an Edge Action to process data, just like our previous Cloud Action, but in this case we can avoid the round trip by sending the edge device our processing instructions and letting the Action Runner do the work.

In the following example we create an observation sending light sensor values to an edge action. The edge action compute the luminosity and send the result to the third line of the LCD.

First create the Observation and send it to the Action Runner.

Beta ea 1 Beta ea 2 Beta ea 3 Beta ea 4

Now lets create the Edge Action.

function(input){
  var lightReading = input.value;
  var lightDescription;
  if (lightReading > 1500) { lightDescription = "bright"} else
  if (lightReading > 300) { lightDescription = "a bit dim"}
  else { lightDescription = "dark"}
  var s = "It's " + lightDescription + " in here.";
  return {"dh://lcd/txt3":[s]  }
}

Beta ea 5 Beta ea 6 Beta ea 7

  1. Select the observation you created before
  2. Enter a name for the edge action
  3. Add your Javascript to process each event from the observation
  4. Create the Edge action. The edge action script is automatically sent to the device to be executed as the device is running in developer mode.

Beta ea 8

Support, Contact, and Feedback

Contacting the team

You can get contact the Octave product team via the Contact us button in the Web UI.

Beta contact

Providing Feddback

You can provide us feedback about things we can improve and ideas of new features on our user feedback tool here (signup required).

Octave API Overview

You can use the API to access Octave API endpoints, which allow you to integrate Octave data into your own solution.

Common Object Members

id

Unique object ID. The first letter of the id denotes the object type.

creationDate

Creation date in milliseconds since epoch.

creatorId

Identity object id for the object's creator.

Company

{
  "id": "c5ada40be09159f4dde3411ff",
  "adminGroupId": "g5ada40be09159f4dde341209",
  "creationDate": 1524252862351,
  "creatorId": "i5ada408e90f45d18eea0d51f",
  "displayName": "Cook County Filtration",
  "name": "cook_county_filtration",
  "usersGroupId": "g5ada40be09159f4dde34120b"
}

A Company is the base level of organization in Octave. Think of a company as a private namespace within Octave that contains its own set of Devices, Streams, and Actions. One or many Identities can have access to a Company. Being a member of a Company is signified by being in its User or Admin group.

adminGroupId

Group containing a list of Identities authorized to administer the Company.

displayName

Mutable, human-friendly Company Name

name

Immutable Company name. Used in API calls and defines the root path under which all company streams reside.

usersGroupId

Group containing a list of Identities in the Company.

Identity

{
  "id": "i5ada408e90f45d18eea0d51f",
  "companies": {
    "c5ada40be09159f4dde3411ff": "/my_company/users/alice"
  },
  "creationDate": 1524252814742,
  "creatorId": "i00000000000000000000000c",
  "email": "an@e.mail",
  "groupIds": [
    "g5b6b19d1f2978f55113a8572"
  ],
  "masterToken": "I0f33E3mdG8VcLIqLlORFUSD4eprGs9o",
  "name": "alice",
  "shares": {
    "h5b577ddfbf37615498230846": {
      "paths": {
        "/my_company": {
          "administer": true
        }
      },
      "roles": [
        "CLOUD_ACTION_READ",
        "CLOUD_ACTION_WRITE",
        "CLOUD_ACTION_DELETE",
        "LOCAL_ACTION_READ",
        "LOCAL_ACTION_WRITE",
        "LOCAL_ACTION_DELETE",
        "BLUEPRINT_READ",
        "BLUEPRINT_WRITE",
        "BLUEPRINT_DELETE",
        "COMPANY_WRITE",
        "COMPANY_DELETE",
        "COMPANY_MEMBERSHIP_EDIT"
      ]
    }
  }
}

Companies

List of Companies to which the Identity belongs.

email

Identity's email address

groupIds

Groups to which the Identity belongs.

masterToken

Authentication Token which inherits all Identity permissions.

name

Unique short name for the Identity, used for created user-owned paths.

shares

Shares which have been granted to the Identity.

Event

{
  "path": "/my_company/my_first_stream",
  "location": {
    "lat": 40.703285,
    "lon": -73.987852
  },
  "elems": {
    "temp": {
      "redSensor": {
        "pressure": {
          "temp": {
            "value": 38.73
          }
        }
      },
      "timestamp": 1.531847525135576E9
    },
    "accel": {
      "x": -0.361192,
      "y": 1.020188,
      "z": 9.7773
    },
    "timestamp": 1.53184752511122E9
  }
}

An Event represents a single entity of data within Octave. It contains a schemaless map of elements. An Event resides within a Stream.

In practice, an Event might be a reading from a light sensor, the response from an external HTTP endpoint, etc. Any complex assortment of related data elements can map to the elements of an Event.

elems

A schemaless map of data elements for the Event.

path

The path of the Stream where the Event is to be written, optional if the streamId is provided in the url of the POST.

location

An optional location for the Event, perhaps corresponding to the location of the device that emitted the Event.

hash

$ curl -XPOST "https://octave-api.sierrawireless.io/v5.0/my_company/event/s53b1d1600cf27b75148de02e" \
  -H "X-Auth-User: <user>" \
  -H "X-Auth-Token: <token>" \
  -d '{
    hash: "uniqueVal",
    elems: {
      measure: 100
    }
  }'

A hash field provides a user-controlled mechanism for enforcing uniqueness within a single Stream. It is somewhat analogous to a guaranteed unique database field, with the important difference that its use is optional.

Set an hash by assigning a unique value to the Event field hash.

Some important points about using hashes:

The hash field can be used, for example, to maintain a single "last seen" value for a given field.

We repeatedly post a changing value to a single Stream with the same hash, posting in succession the following values:

{"hash":"currentValue", "elems":{"measure":100}}

{"hash":"currentValue", "elems":{"measure":200}}

{"hash":"currentValue", "elems":{"measure":300}}

{"hash":"currentValue", "elems":{"measure":400}}

after each post the Stream will hold only a single Event holding the value {"measure" : 400}. The Event creation date will reflect the time of the last write.

Stream

{
  "filter": "EXISTS service_available",
  "path": "/my_company/raw_messages",
  "description": "Raw data from devices, must contain at least a service_available element",
  "capacity": 1000
}

Events live in a Stream. A Stream has an event capacity and is similar to a ring buffer. A Stream exists within a namespace specified by its path field.

description

Optional human-readable description.

path

Streams exist within an hierarchical namespace. Its location within the namespace is its path. The path is always starts with a / and a company name, and at least one other identifier, with each identifier is separated by a /, similar to a file system.

capacity

The maximum number of Events that can be stored within the Stream. The default value is 1,000, maximum is 100,000. When the Stream exceeds its capacity of Events, older Events will be deleted automatically.

filter

Filters are used to require that Events written to a Stream have specific data elements and values. See the filtering documentation for for more information.

Cloud Action

{
  "description": "my cool project",
  "source": "/my_company/my_cool_project/incoming",
  "filter": "elems.error_rate > 0.1",
  "js": "function(input) {
    input.elems['needsAttention'] = true;
    return {
      '/my_company/my_cool_project/high_priority': [input]
    };
    }"
}

A Cloud Action transforms Events and routes them between Streams. Transformations are done via optional JavaScript.

A Cloud Action specifies a source Stream and either a js function or a destination Stream. If js is not provided, the Cloud Action will act as a "dumb pipe". If js is provided, the incoming Event from the source Stream is evaluated against the js function.

description

Optional human-readable description.

source

The source Stream path or tag-path. Events that are published into this Stream will be monitored by the Cloud Action.

destination

Must be specified if js is unspecified. An Event written to source will be written here, subject to filtering.

filter

Filters whether an Event is evaluated by the Cloud Action. See the filtering documentation for for more information.

js

Must be specified if destination is unspecified. The function must take an Event as input and return a map of destinations to list of Events.

Device

{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "d5b3ab093665c5454445234f4",
    "avSystemId": "b6961c66c9b544b787d0065f5d193326",
    "blueprintId": {
      "id": "b5b3ab9d056525434445264b3",
      "version": 44
    },
    "creationDate": 1530572947298,
    "creatorId": "i5b2903f6ff127c1f9d80c601",
    "dirty": true,
    "displayName": "My MangOH Red",
    "lastEditDate": 1535122524739,
    "lastSeen": 1535381785410,
    "localActions": {
       "l000000000000000000000000": {
          "version": 5
        }
    },
    "localVersions": {
      "batteryService": "1.0",
      "blueprintVersion": 0,
      "io": "0.0.1",
      "cloudInterface": "0.0.22",
      "lcd": "1.0.0",
      "blueprintId": "",
      "dataHub": "656360ee655c81c791a741fbce767107",
      "redSensor": "3.0",
      "util": "1.0.0",
      "changeDate": 1535122524739,
      "location": "0.0.1",
      "actionRunner": "0.0.1",
      "firmware": "SWI9X06Y_02.14.04.00"
    },
    "location": {
      "lat": 40.704067,
      "lon": -73.989088
    },
    "name": "my_mangoh_red",
    "observations": {
      "/util/cellular/signal/value": {
        "rx": {
          "destination": "store"
        },
        "er": {
          "destination": "cloudInterface"
        },
        "ecio": {
          "destination": "store"
        }
      }
    },
    "path": "/my_company/devices/my_mangoh_red",
    "state": {
      "/util/cellular/signal/period": 3600,
      "/cloudInterface/developer_mode/enable": false,
      "/cloudInterface/store_forward/period": 43200,
      "/util/cellular/signal/enable": true
    },
    "summary": {
      "/redSensor/pressure/temp/trigger": {
        "dt": "trigger",
        "t": "output",
        "m": false
      },
      "/util/cellular/statistics/trigger": {
        "dt": "trigger",
        "t": "output",
        "m": false
      },
      "/cloudInterface/connected/value": {
        "dt": "boolean",
        "t": "input",
        "m": false,
        "v": true,
        "ts": 1535122655000
      }
    },
    "tags": { "location": "nyc" },
    "timeSinceLastSeen": 10923849,
    "synced": true
  }
}

A Device represents a physical device in the world. It contains attributes that the user will set to configure the physical device, and to assign application code which it will execute (in the form of Edge Actions).

The Device object also contains attributes populated by the physical device, indicating its status, the software deployed to the device, and all of its capabilities.

blueprintId

The Blueprint (and version) assigned to the Device.

dirty

Indicates that the device configuration differs from the assigned Blueprint, or that no Blueprint is assigned.

displayName

A friendly name for the Device.

lastSeen

The time the Device last communicated to the cloud.

localActions

Defines the Local Actions (represented by IDs and Versions) to be deployed to the Device.

localVersions

Reported from the Device; which software versions are currently deployed.

location

The location of the Device, set automatically if a GPS / GNSS resource is observed, otherwise set manually.

name

Device name. Final.

observations

A collection of "routes" from Resources to other Resources, Local Actions, or the Cloud.

path

The path member can be a string of up to 128 characters.

state

The default values assigned to Resources on the Device.

summary

A collection of Resources available on the Device, their types and their latest values.

tags

Map of "tags", to group and categorizes collections of Devices. This map is of type string -> string.

timeSinceLastSeen

Milliseconds since the Device last communicated with the cloud.

synced

Indicates that the configuration loaded on the Device matches what is defined in this object.

Blueprint

{
  "id": "b000000000000000000000000",
  "companyId": "c000000000000000000000000",
  "creationDate": 1530575312073,
  "creatorId": "i000000000000000000000000",
  "displayName": "My Blueprint",
  "lastEditDate": 1532118204711,
  "localActions": {
    "l000000000000000000000000": {
      "version": 1
    }
  },
  "observations": {
    "/redSensor/position/value": {
      "position": {
        "period": 3600,
        "destination": "store",
        "description": "Send Position Periodically"
      }
    }
  },
  "state": {
    "/redSensor/position/period": 60
  },
  "version": 44
}

Blueprints capture a specific Device configuration as a template, which can be assigned quickly to multiple Devices, ensuring they all share the same configuration and behavior.

Configuration includes the Resource state, Observations and Edge Actions assigned to a Device.

Blueprints are versioned and maintain a version history. Common operations include:

A Blueprint alone does nothing - it must be assigned to a Device for it to take effect.

displayName

A friendly name for the Blueprint.

localActions

Defines the Local Actions (represented by IDs and Versions) to be deployed to the Device.

observations

A collection of "routes" from Resources to other Resources, Local Actions, or the Cloud.

state

The default values assigned to Resources on the Device.

Edge Action

An Edge Action is like a Cloud Action but runs on physical devices using the "Octave" Edge Package.

An Edge Action transforms Events from Observations and routes them to other Resources, or to the Cloud. Transformations are done via JavaScript. When an Edge action is assigned to a Device, the JavaScript is sent to the Device and loaded. It is bound to a specific Observation, such that when a new Event is created from the Observation, the JavaScript is executed with the Event as the input parameter.

description

Optional human-readable description.

source

The name of the Observation which triggers execution of the Edge Action. Always prefixed with observation://.

js

The function must take an Event as input and return a map of destinations to list of Events.

Group

{
  "displayName": "Research Team",
  "description": "Members of Project Team Y",
  "memberIds": [
    "i5488db43d4c63a633ad84bcc",
    "i5489e275d4c6f62700be9fd6"
  ]
}

A Group is a collection of identities. Groups exist for the purpose of issuing Shares to them.

A Group's memberIds field is mutable. Adding or removing an Identity from a Group will update that Identity's permissions automatically.

memberIds

A list of Identities corresponding to the members of the Group.

displayName

A descriptive name for the Group

description

Further information used to describe the Group

Share

 {
  "issuedTo": "i548b1c1bd4c607c7716e4ac2",
  "paths": {
    "/my_company/foo" : {
      "read" : true,
      "write": true,
      "eventRead": true,
      "eventWrite": true
    },
    "/my_company/bar" : {
      "read" : true,
      "write": false,
      "eventRead" : true,
      "eventWrite": false
    }
    }
}

A Share grants access to particular resources and actions to existing Identities. This access is provided through a combination of path-based permissions and non-path-based "roles". Shares may be revoked at any time. They also can be timebound.

issuedTo

An id corresponding to either the Identity or the Group being issued the Share. The Identity or Group being issued the Share must be a member of the Company in which the Share is being created.

paths

A map of paths and tag-paths to their Permissions. In this way, each path can have a unique permission set. Permissions are recursive.

roles

A list of additional, non-path-based actions available to the Identity.

duration

Lifespan of Share in milliseconds.

Token

A Token provides anonymous Event read and write access to a subset of your Company's namespace. They are ideally suited, for example, for web applications which need Event read or write access only to a few specific Streams. Tokens may be revoked at any time. Tokens may also have a duration.

To use a Token, supply the Token String in the same way as you would supply the Master Token - within the X-Auth-Token header and omit the X-Auth-User header.

{
  "tokenString": "SSOjDZ4VMHS2JcwT1sIpE8x91QfG",
  "paths": {
    "/my_company/foo" : {
        "eventRead" : true,
        "eventWrite": true
  },
    "/my_company/bar" : {
        "eventRead" : false,
        "eventWrite": true
    }
  }
}

A Token provides anonymous Event read and write access to a subset of your Company's namespace. They are ideally suited, for example, for web applications which need Event read or write access only to a few specific Streams. Tokens may be revoked at any time. Tokens may also have a duration.

To use a Token, supply the Token String in the same way as you would supply the Master Token - within the X-Auth-Token header and omit the X-Auth-User header.

paths

A map of paths and tag-paths to their Permissions. In this way, each Path can have a unique Permission set. Permissions are recursive.

duration

Lifespan of Token in milliseconds.

Authentication

REST and WebSocket requests are authorized using an account name and token combination.

Each account has a Master Token which can be retrieved via the Octave User Interface.

To retrieve your master token, go to the lower left of the user interface and select “Master Token”.

Retrieve Account info with Master Token

curl "https://octave-api.sierrawireless.io/v5.0/global/identity" \
  -H "X-Auth-User: <your_user_name>" \
  -H "X-Auth-Token: <your_token>"

The above will return:

{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": [
    <your identity object>
  ]
}

You can use the /global/identity endpoint to easily retrieve all information about your account, including all of the groups, companies, and shares you belong to.

HTTP Request

GET https://octave.sierrawireless.io/global/identity

Edge Application Development

Edge Applications


// How to Observe the Temperature
"/sensors/temperature/value": {
  "my_temperature_observation": {

    // Every 60 seconds
    "period" : 60,

    // Only if greater than or equal to 30 deg C
    "gte" : 30,

    // Send directly to the Cloud
    "destination" : "cloudInterface"
  }
}

Octave allows developers to deploy powerful applications to edge devices without the need to do system programming in a low level language such as C or C++.

Sensors and Actuators attached to the device are treated as first class services known as Resources. It is easy to read from and write to Resources, as though you were reading directly from a file or socket.

Octave leverages the Event Driven model of writing software. Sensors are simply sources (or Resources in our terminology) that generate readings (Events). These Events flow through a pipeline that might contain filters, buffers or rate-limiters (Observations). You may wish to execute arbitrary JavaScript code on Events in the pipeline (Edge Actions), which can transform and perform conditional logic. Finally, Events can be pushed directly to Actuators or sent to the Cloud for further processing.

Rather than writing procedural system-level C code to read a sensor, Octave provides declarative, rules based Observations for defining how and when the sensor should be read, and where the readings should be pushed. Declarative rules are easier to reason about, easily testable, and more flexible than procedural code.

Once the Event has be generated, it can:

Moving data from Device to Cloud is a critical use case for most developers. With Octave, sending data to the Cloud can be as simple as setting a single attribute in an Observation. Developers can also instruct when the data should be sent - immediately, or store and forward when the device next heartbeats.

Glossary

Resource

A sensor (input), an actuator (output), or a variable for storage or configuration. Similar to the unix filesystem.

Resource Tree

The collection of Resources available on a Device

Resource Configuration

The initial value a Resource will take when the Device is powered on, used to configure sensors and actuators.

Event

A timestamped value generated from a Resource (such as a sensor) or sent to a Resources (such as an actuator).

Observation

A pipe from a Resource to another Resource, the Cloud, buffer, or JavaScript function. Events will flow through the Observation

Edge Action

Arbitrary JavaScript function, executed when an Event is generated from an Observation. Can transform or generate further Events.

Store and Forward

Events will be held locally for a set period, before sending as a batch to the Cloud.

Connectivity

Your physical device is not always connected to the cellular network. Cellular connectivity increases power and data consumption of the device, so we want to be certain it is only connected when required.

There are some key concepts to explain how and when a Device is connected:

Pushing Events to the Cloud

If and when an Event is ready to be pushed directly to the Cloud, we immediately establish a connection. The connection will stay open if further Events are available within a short period of time, otherwise it will be closed.

Store and Forward

If the user is using Store and Forward to send Events to the Cloud, we will wait until the specified time period before establishing a connection and sending all stored Events.

Heartbeat

By default, the Device will establish a connection after a specified timeout, regardless of Events waiting to be sent to the Cloud, in order to check in with the server. This can be configured or disabled.

Pushing Configuration to the Device

Configuration changes (Resource Configuration, Observations and Edge Action changes) can only be delivered to the Device when a connection is open.

Developer Mode

The Device, by default, is configured to be in Developer Mode. This keeps a connection open with the server continuously, so that Device Configuration (see above) can be delivered immediately. During Developer Mode, the Device also frequently transmits the Resource Tree to the Octave servers, so the latest information about the Device is available to the user. By default, Developer Mode will remain enabled so long as Device Configuration is being pushed to the Device. After a period of inactivity, it will be disabled and connectivity management will follow the rules described above.

Developer Mode is useful for when writing, testing and debugging Edge applications. In deployment, devices should have Developer Mode disabled.

Device Resources

A Resource is a node in a Resource Tree, representing a "thing" that creates, receives, or stores Events. Because it lives in a tree, each Resource will have a Path that identifies its location, such as /sensors/light/value or /lcd/line1.

Resources have a type - they are either Inputs or Outputs. Inputs will generate new Events from the underlying application or hardware. Sensors are tied to inputs. Outputs will forward Events to the application or Hardware. Actuators are outputs.

A Sensor or Actuator will normally expose multiple Resources, for direct access to the hardware, and configuration. Here is an example:

Path Type Data Type Mandatory Default Value
/sensor/light/value input numeric - -
/sensor/light/enable output boolean false false
/sensor/light/period output numeric true -
/sensor/light/calibration output json false {min: 1234, max:5678}

The value Resource is the standardized naming convention for the Resource which generates the Events the Sensor is named for. So in this example, /sensor/light/value will generate light readings.

The Sensor also exposes other resources for configuration. The period Resource accepts a numeric value (in seconds) for how frequently the hardware should be polled, and emit a new Event on the value Resource. Interrupt-based sensors, such as alarms, might not have this Resource. The Sensor driver has marked this Resource mandatory, indicating that a numeric value must be sent to this Resource for the Sensor to work.

The enable Resource allows the system to turn the Sensor on or off.

The calibration Resource allows the system to provide calibration information to the driver. It is marked non-mandatory, so the Sensor can be used without providing this information. If a value is not provided, it will default to the Default Value. The Resource has a Data Type json, indicating that it receives a string value containing valid JSON.

// The Device summary attribute lists the Resources
"summary": {
  "/redSensor/light/enable": {
    "dt": "boolean",
    "t": "output",
    "m": true,
    "v": true,
    "ts": 1535045368000
  },
  "/redSensor/light/value": {
    "dt": "numeric",
    "t": "input",
    "m": false,
    "v": 1700,
    "ts": 1535466998000
  },
  "/redSensor/light/period": {
    "dt": "numeric",
    "t": "output",
    "m": true,
    "v": 5,
    "ts": 1535045403000
  }
  ...
}

// The Device state attribute sets the values
"state" {
  "/redSensor/light/enable" : true,
  "/redSensor/light/period" : 60
}

Your Device will initially broadcast its Resource Tree, such that it is available to view using the Octave REST API, on the Device object. See the Devices page for more details.

Resource Configuration

Before the Light Sensor can do work, we must configure the output Resources enable and period. To do so, we must push a new Event to the Resource. We have two methods of doing this:

  1. Push the value now
  2. Push the value now, and every time the Device powers on

Option 2 is our more preferred approach, and involves adding our values to the Device.state object.

The Device object has a state attribute, which contains a map of keys (Resource path) and associated values. Any updates to Resource Configuration, Observations or Edge Actions will be sent to the Device when it is next connected to the cellular network, and then persisted locally such that it will survive restarts.

Observations

{
  "/redSensor/light/value" : {
    "my_observation" : {
      "destination" : "cloudInterface",
      "period" : 200,
      "gte" : 32.1
    }
  }
}

Once we have configured and enabled our Light Sensor, the Sensor will begin taking readings, but those readings will not go anywhere or do anything. We must create an Observation. Some things we may want an Observation to do:

Observations have the following properties:

Property Mandatory Data Type Example Description
Source Yes String /sensors/light/value The Resource which generates the Events
Name Yes String my_observation A unique name
Destination No String cloudInterface Where to send the Events. If empty, the will be stored for later querying
Period No Integer 200 Rate-limits incoming Events
Less Than Equal No Float 15.5 Forward only if Event value is less than or equal to this threshold
Greater Than Equal No Float 32.1 Forward only if Event value is greater than or equal to this threshold
Step No Float 2.0 Forward only if Event value has changed by at least this threshold
Select No String x For Events that are JSON: extract this key from the Event

Sending an Individual Command

{
  "redSensor": {
    "light": {
      "period":10
    }
  }
}

To send an individual command to a device, such a setting a resource value or configuration, add an Event to the /<company_name>/devices/<device_name>/:command Stream. You address the resource by transforming the resource path into an object.

Analog & Digital IO

IO Service

The Octave edge package provides an IO Service which enables remote and dynamic configuration of the analog and digital inputs and outputs present on the edge device.

Connecting IO pins to Octave is as simple as sending a configuration to the IO Service that defines the settings for the IO pin you'd like to connect (directionality, internal resistors, etc), and how that IO should be mapped as a Resource.

IO Service Overview

Types of Resources

Digital Input (GPIO)

GPIO pins configured as inputs in Octave will report their value as true or false depending on if the voltage is above or below the midpoint of a voltage range (within some tolerance). This range varies by pin and will be specified below. Internal pull-up or pull-down resistors can also be enabled.

If edge detection is enabled, the IO Service will update the value of the digital input and emit an event to any Observation attached to the Resource any time the state changes from high to low, or vice versa. Without edge detection, you must configure a period on your IO Resource and the IO Service will report a boolean value at the specified periodicity. This "interrupt" functionality can be very useful when detecting events like a button push or switch throw that you might otherwise miss between polls when observing boolean values at a set period.

Digital Output (GPIO)

Digital Output pins are also represented as boolean values. When these Resources are set, they drive a low or high output voltage over the pin. Common use cases for Digital Output pins include lighting an LED or controlling a relay.

Analog Input (ADC)

Depending on your hardware, 1 or more Analog to Digital Converters are available for converting the sensed voltage on a particular pin to a numeric Resource value.

Identifying GPIO Pins

MangOH Red "Hat"

The RPi Hat pins on the MangOH Red can be used to connect Digital Input and Output. The diagram below illustrates the GPIO pins available to the Octave IO Service (WP_GPIO 1, 2, 3, and 8) as well as current sources and ground. The voltage range for Digital Inputs on these pins is 3.3 volts.

MangOH Red Hat GPIO on Octave

IoT Card Proto Board

Your MangOH red kit will come with a prototyping board that fits into the IoT Expansion Slot. This pins on this card provide access to both digital input/output and a single analog input. The diagram below illustrates the GPIO pins available to the Octave IO Service (GPIO 1, 2, 3, 4 and ADC0) as well as current sources and ground. The voltage range for Digital Inputs on these pins is 1.8 volts.

MangOH Red IoT Expansion on Octave

MangOH Low Power IO

The MangOH Red CN312 pin out has two additional Analog inputs, as well as a 1.8 volt current source. The diagram below documents the Low Power IO pinout.

MangOH Red Low Power IO on Octave

Configuring (GUI)

The IO Service can be configured at by navigating to Device -> Services and selecting "Configure the IO Service on this Device".

Device Services Menu

Next select "configure the io service on this device"

Configure IO Button

In the IO Service configuration view, you can select the type of input to add.

Add IO Button

On the next screen, you'll want to pay attention to the "RESOURCE PATH" option. By default the Resource created by the IO service will share the name of the pin that it connects. However, it's usually much more practical to use short name that describes what you are actually connecting. For example red_led might make sense for a digital output connected to an LED, while photoresistor might make sense for an analog input name.

Configure IO Digital In

Once you've configured your IO you'll return to the IO Service screen, with your newly created IO item listed. Note that the path provided in the previous step as been prepended with /io.

Configured IO

If we look under /io in the Resource screen we should see our newly added IO has appeared as a Resource. From here we can treat this Resource like any other. We'll likely want to configure it, set a polling period, and follow that with some Observations.

Configured Resource

Device Object

Creating a Device

Reading a Device

## Read Device
curl "https://octave-api.sierrawireless.io/v5.0/my_company/device/d5b730d2f6f3861bb4e95f51c" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "d5b3ab093665c5454445234f4",
    "avSystemId": "b6961c66c9b544b787d0065f5d193326",
    "blueprintId": {
      "id": "b5b3ab9d056525434445264b3",
      "version": 44
    },
    "creationDate": 1530572947298,
    "creatorId": "i5b2903f6ff127c1f9d80c601",
    "dirty": true,
    "displayName": "My MangOH Red",
    "lastEditDate": 1535122524739,
    "lastSeen": 1535381785410,
    "localActions": {
       "l000000000000000000000000": {
          "version": 5
        }
    },
    "localVersions": {
      "batteryService": "1.0",
      "blueprintVersion": 0,
      "io": "0.0.1",
      "cloudInterface": "0.0.22",
      "lcd": "1.0.0",
      "blueprintId": "",
      "dataHub": "656360ee655c81c791a741fbce767107",
      "redSensor": "3.0",
      "util": "1.0.0",
      "changeDate": 1535122524739,
      "location": "0.0.1",
      "actionRunner": "0.0.1",
      "firmware": "SWI9X06Y_02.14.04.00"
    },
    "location": {
      "lat": 40.704067,
      "lon": -73.989088
    },
    "name": "my_mangoh_red",
    "observations": {
      "/util/cellular/signal/value": {
        "rx": {
          "destination": "store"
        },
        "er": {
          "destination": "cloudInterface"
        },
        "ecio": {
          "destination": "store"
        }
      }
    },
    "path": "/my_company/devices/my_mangoh_red",
    "state": {
      "/util/cellular/signal/period": 3600,
      "/cloudInterface/developer_mode/enable": false,
      "/cloudInterface/store_forward/period": 43200,
      "/util/cellular/signal/enable": true
    },
    "summary": {
      "/redSensor/pressure/temp/trigger": {
        "dt": "trigger",
        "t": "output",
        "m": false
      },
      "/util/cellular/statistics/trigger": {
        "dt": "trigger",
        "t": "output",
        "m": false
      },
      "/cloudInterface/connected/value": {
        "dt": "boolean",
        "t": "input",
        "m": false,
        "v": true,
        "ts": 1535122655000
      }
    },
    "tags": { "location": "nyc" },
    "timeSinceLastSeen": 10923849,
    "synced": true
  }
}

HTTP Request

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/device/<device_identifier>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Updating a Device

curl -X "PUT" "https://octave-api.sierrawireless.io/v5.0/my_company/device/d5b730d2f6f3861bb4e95f51c" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
    "displayName": "Testing MangOH Red",
    "state": {
      "/util/cellular/signal/period": 7200,
      "/cloudInterface/developer_mode/enable": true,
      "/cloudInterface/store_forward/period": 43200,
      "/util/cellular/signal/enable": true
    }
}'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "d5b730d2f6f3861bb4e95f51c",
    ... <snip> ...
    "state": {
      "/util/cellular/signal/period": 7200,
      "/cloudInterface/developer_mode/enable": true,
      "/cloudInterface/store_forward/period": 43200,
      "/util/cellular/signal/enable": true
    }
  }
}

HTTP Request

PUT https://octave-api.sierrawireless.io/v5.0/<company_name>/device/<device_identifier>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Request JSON Object

For a full overview of all Device attributes, see the Device Object Overview.

Key Type Value
displayName string A friendly name for the Device
state object The default values assigned to Resources on the Device

Deleting a Device

curl -X "DELETE" "https://octave-api.sierrawireless.io/v5.0/my_company/device/d5b730d2f6f3861bb4e95f51c" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [
      "Your request has been processed successfully. The requested resource has been deleted."
    ],
    "errors": [],
    "references": {}
  },
  "body": {}
}

HTTP Request

DELETE https://octave-api.sierrawireless.io/v5.0/<company_name>/device/<device_identifier>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Stream Object

Creating a Stream

curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/my_company/stream" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
  "path": "/my_company/streamName",
  "description": "My New Stream"
}'

{
  "head": {
    "status": 201,
    "ok": true,
    "messages": [
      "Your request has been processed successfully. A new resource has been created."
    ],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "s5b730d2f6f3861bb4e95f51c",
    "capacity": 1000,
    "companyId": "c000000000000000000000006",
    "creationDate": 1534266671309,
    "creatorId": "i000000000000000000000064",
    "description": "My New Stream",
    "path": "/my_company/streamname"
  }
}

You can create a stream at any path where you have permissions.

HTTP Request

POST https://octave-api.sierrawireless.io/v5.0/<company_name>/stream

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Request JSON Object

Key Type Value
path string Required. The path member can be a string of up to 128 characters
capacity int Optional; defaults to 1,000. The maximum number of Events stored within the Stream.
description string Optional. Human-friendly description
filter string Optional. A filter which is used as a pass/fail test for writing new Events to the Stream.

Reading a Stream

## Read Stream
curl "https://octave-api.sierrawireless.io/v5.0/my_company/stream/s5b730d2f6f3861bb4e95f51c" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "s5b730d2f6f3861bb4e95f51c",
    "capacity": 100000,
    "companyId": "c000000000000000000000006",
    "creationDate": 1534266671309,
    "creatorId": "i000000000000000000000064",
    "description": "My New Stream",
    "lastEditDate": 1534266950758,
    "path": "/my_company/streamname"
  }
}

HTTP Request

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/stream/<stream_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Updating a Stream

curl -X "PUT" "https://octave-api.sierrawireless.io/v5.0/my_company/stream/s5b730d2f6f3861bb4e95f51c" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
  "capacity": 100000
}'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "s5b730d2f6f3861bb4e95f51c",
    "capacity": 100000,
    "companyId": "c000000000000000000000006",
    "creationDate": 1534266671309,
    "creatorId": "i000000000000000000000064",
    "description": "My New Stream",
    "lastEditDate": 1534266950758,
    "path": "/my_company/streamname"
  }
}

HTTP Request

PUT https://octave-api.sierrawireless.io/v5.0/<company_name>/stream/<stream_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Request JSON Object

Key Type Value
capacity int The maximum number of Events stored within the Stream.
description string Human-friendly description
filter string A filter which is used as a pass/fail test for writing new Events to the Stream.

Deleting a Stream

curl -X "DELETE" "https://octave-api.sierrawireless.io/v5.0/<company_name>/stream/s5b730d2f6f3861bb4e95f51c" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [
      "Your request has been processed successfully. The requested resource has been deleted."
    ],
    "errors": [],
    "references": {}
  },
  "body": {}
}

HTTP Request

DELETE https://octave-api.sierrawireless.io/v5.0/<company_name>/stream/<stream_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Event Object

Creating an Event

curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/my_company/event/s5b7310ae6f38613585853e5b" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
  "elems": {
    "measure": 7
  }
}'
{
  "head": {
    "status": 201,
    "ok": true,
    "messages": [
      "Your request has been processed successfully. A new resource has been created."
    ],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "e5b7311856f38613585853e61",
    "streamId": "s5b7310ae6f38613585853e5b",
    "creationDate": 1534267781805,
    "generatedDate": 1534267781805,
    "path": "/my_company/streamname",
    "version": 0,
    "elems": {
      "measure": 7
    }
  }
}

You can create an event in any stream for which you have eventWrite permission.

HTTP Request

POST https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Query Parameters

Parameter Default Description
refs 0 0 or 1, to disable/enable return of objects referenced by the created object

Request JSON Object

Key Type Value
elems object Schemaless event data

Reading an Event

curl "https://octave-api.sierrawireless.io/v5.0/my_company/event/s5b7310ae6f38613585853e5b/e5b7311856f38613585853e61" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "e5b7311856f38613585853e61",
    "streamId": "s5b7310ae6f38613585853e5b",
    "creationDate": 1534267781805,
    "generatedDate": 1534267781805,
    "path": "/my_company/streamname",
    "version": 0,
    "elems": {
      "measure": 7
    }
  }
}

HTTP Request

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>/<event_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Query Parameters

Parameter Default Description
refs 0 0 or 1, to disable/enable return of objects referenced by the created object

Updating an Event

curl -X "PUT" "https://octave-api.sierrawireless.io/v5.0/my_company/event/s5b7310ae6f38613585853e5b/e5b7311856f38613585853e61" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
  "elems": {
    "measure": 10
  }
}'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "e5b7311856f38613585853e61",
    "streamId": "s5b7310ae6f38613585853e5b",
    "lastEditDate": 1534268183964,
    "path": "/my_company/streamname",
    "version": 0,
    "elems": {
      "measure": 10
    }
  }
}

HTTP Request

PUT https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>/<event_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Query Parameters

Parameter Default Description
refs 0 0 or 1, to disable/enable return of objects referenced by the created object

Request JSON Object

Key Type Value
elems object Schemaless event data

Deleting an Event

curl -X "DELETE" "https://octave-api.sierrawireless.io/v5.0/my_company/event/s5b7310ae6f38613585853e5b/e5b7311856f38613585853e61" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [
      "Your request has been processed successfully. The requested resource has been deleted."
    ],
    "errors": [],
    "references": {}
  },
  "body": {}
}

HTTP Request

DELETE https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>/<event_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Find Events By Stream ID

curl "https://octave-api.sierrawireless.io/v5.0/my_company/event/s5b7310ae6f38613585853e5b" \
    -H 'x-auth-user: <user>' \
    -H 'x-auth-token: <token>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": [
    {
      "id": "e5b7311856f38613585853e61",
      "streamId": "s5b7310ae6f38613585853e5b",
      "creatorId": "i000000000000000000000001",
      "metadata": null,
      "creationDate": 1534267781805,
      "generatedDate": 1534267781805,
      "lastEditDate": null,
      "path": "/my_company/streamname",
      "hash": null,
      "elems": {
        "measure": 7
      }
    },
    [...]
  ]
}

HTTP Request

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Find Events By Stream Path

curl "https://octave-api.sierrawireless.io/v5.0/my_company/event?path=/my_company/streamname" \
    -H 'x-auth-user: <user>' \
    -H 'x-auth-token: <token>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": [
    {
      "id": "e5b7311856f38613585853e61",
      "streamId": "s5b7310ae6f38613585853e5b",
      "creatorId": "i000000000000000000000001",
      "metadata": null,
      "creationDate": 1534267781805,
      "generatedDate": 1534267781805,
      "lastEditDate": null,
      "path": "/my_company/streamname",
      "hash": null,
      "elems": {
        "measure": 7
      }
    },
    [...]
  ]
}

HTTP Request

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event?path=<stream_path>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Query Parameters

Parameter Description
path Required. The stream path.

Cloud Action Object

Creating a Cloud Action

curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/my_company/action" \
    -H 'X-Auth-Token: <token>' \
    -H 'X-Auth-User: <user>' \
    -H 'Content-Type: application/json; charset=utf-8' \
    -d $'{
    "js": "function(event) { return {\\"/my_company/destination\\": [event]} }",
    "source": "/my_company/source"
    }'
{
  "head": {
    "status": 201,
    "ok": true,
    "messages": [
      "Your request has been processed successfully. A new resource has been created."
    ],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "a5b7ef06757d988c2d22e6165",
    "companyId": "c000000000000000000000006",
    "creationDate": 1535045735793,
    "creatorId": "i000000000000000000000064",
    "js": "function(event) { return {\"/my_company/destination\": [event]} }",
    "source": "/my_company/source",
    "version": 1
  }
}

Permission to create a Cloud Action is governed by the CLOUD_ACTION_WRITE role.

HTTP Request

POST https://octave-api.sierrawireless.io/v5.0/<company_name>/action

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Query Parameters

Parameter Default Description
refs 0 0 or 1, to disable/enable return of objects referenced by the created object

request json object

key type description
description string optional. human-friendly description
destination string optional. event output path.
disabled bool is action disabled?
source string required. path from which to read events.
js string optional. javascript to execute.
filter string optional. a filter which is used as a pass/fail test for evaluating whether to run new events on the source stream through the cloud action

Reading a Cloud Action

curl "https://octave-api.sierrawireless.io/v5.0/my_company/action/a5b7337e76f38615fb24a4ea8" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "a5b7ef06757d988c2d22e6165",
    "companyId": "c000000000000000000000006",
    "creationDate": 1535045735793,
    "creatorId": "i000000000000000000000064",
    "js": "function(event) { return {\"/my_company/destination\": [event]} }",
    "source": "/my_company/source",
    "version": 1
  }
}

Permission to read Cloud Actions is governed by the CLOUD_ACTION_READ role.

HTTP Request

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/action/<action_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Query Parameters

Parameter Default Description
refs 0 0 or 1, to disable/enable return of objects referenced by the created object

Updating a Cloud Action

curl -X "PUT" "https://octave-api.sierrawireless.io/v5.0/my_company/action/a5b7337e76f38615fb24a4ea8" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
  "description": "My Cloud Action"
}'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "a5b7ef06757d988c2d22e6165",
    "companyId": "c000000000000000000000006",
    "creationDate": 1535045735793,
    "creatorId": "i000000000000000000000064",
    "description": "My Cloud Action",
    "js": "function(event) { return {\"/my_company/destination\": [event]} }",
    "lastEditDate": 1535046500099,
    "source": "/my_company/source",
    "version": 2
  }
}

Permission to update a Cloud Action is governed by the CLOUD_ACTION_WRITE role.

HTTP Request

PUT https://octave-api.sierrawireless.io/v5.0/<company_name>/action/<action_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Query Parameters

Parameter Default Description
refs 0 0 or 1, to disable/enable return of objects referenced by the created object

request json object

key type description
description string optional. human-friendly description
destination string optional. event output path.
disabled bool is action disabled?
source string required. path from which to read events.
js string optional. javascript to execute.
filter string optional. a filter which is used as a pass/fail test for evaluating whether to run new events on the source stream through the cloud action

Deleting a Cloud Action

curl -X "DELETE" "https://octave-api.sierrawireless.io/v5.0/my_company/action/a5b7337e76f38615fb24a4ea8" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [
      "Your request has been processed successfully. The requested resource has been deleted."
    ],
    "errors": [],
    "references": {}
  },
  "body": {}
}

Permission to delete a Cloud Action is governed by the CLOUD_ACTION_DELETE role.

HTTP Request

DELETE https://octave-api.sierrawireless.io/v5.0/<company_name>/action/<action_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Simulating a Cloud Action

curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/my_company/action/simulate" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
  "event": {
    "elems": {
      "a": 1
    }
  },
  "js": "function (input_event) {
           var a = input_event.elems.a;
           input_event.elems.b = a  * 3;
           return {
             \\"/my_company/output\\": [input_event]
           };
         }"
}'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "events": {
      "/my_company/output": [
        {
          "id": "e5b88567457d988734c4078d1",
          "metadata": null,
          "creationDate": 1535661684244,
          "lastEditDate": null,
          "generatedDate": null,
          "path": null,
          "location": null,
          "hash": null,
          "elems": {
            "a": 1,
            "b": 3
          }
        }
      ]
    },
    "log": [
      {
        "a": 1,
        "line_no": 2,
        "input_event": {
          "elems": {
            "a": 1
          }
        }
      },
      {
        "line_no": 3,
        "input_event": {
          "elems": {
            "a": 1,
            "b": 3
          }
        }
      }
    ],
    "errors": []
  }
}

This endpoint can be called on either an existing or non-existing Cloud Action. If being called on a non-existing Cloud Action, the js field must be provided. The endpoint evaluates Cloud Action JavaScript against a given Event. The simulator output will contain the function return value (a map of destinations to list of Events), as well as a log, which is a list of the variables of your running script on each line number in the order they were executed (variables that are unchanged will not be reprinted).

HTTP Request

POST https://octave-api.sierrawireless.io/v5.0/<company_name>/action/simulate

POST https://octave-api.sierrawireless.io/v5.0/<company_name>/action/<action_id>/simulate

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

request json object

key type description
js string optional - see note above. function to execute
event map required. event to pass to function
mocks map optional. mocked responses from external services

Mocks

Cloud Action simulate takes an optional mocks object for mocking out responses from external services, such as HTTP calls. Below is a sample mock map.

{
  "http://foo.com": {"data":"somedata", "method":"POST","status":200,"message":"OK"}
}

The key of the map must match the path being used in the external service call. If a mock map is provided and its keys do not match the path of a particular external service call, an error will be generated.

Cloud JavaScript Library

Overview

Octave.js is our server side javascript processing environment. It gives you a library under the Octave namespace. It also executes your code on our servers, ensuring that your code executes safely–both for you and for everyone else on our platform.

Octave.js provides methods for CRUD operations on select Octave domain objects from within Cloud Actions. These methods are available in the global namespace 'Octave'. The 'Octave' namespace provides 'find' and 'get' under the subnamespaces Stream, Event, and Action. Event also includes 'get', 'find', 'findOne', 'findHash' and 'aggregate' methods.

The Octave namespace also houses a custom error object, Octave.Error, which is thrown when a fatal error is encountered during the invocation of certain methods. Octave.Error objects have two properties, "message" and "details". "message" is a string value that represents the general cause of error. "details" is an array of strings that represent more specific information relating to the cause of error.

Processing Environment

Strict Mode

The environment that executes your code in strict mode, a restricted subset of javascript. Generally, this shouldn't change that much about how you use javascript. But, it does change some things. Most noticeably, you should avoid declaring global variables (use var x = 1 instead of just simply x = 1). For more information about strict mode, I'd recommend reading the Mozilla Developer Network article.

ECMAScript 6

We do not support most features of ECMAScript 6. We also disallow the use of eval.

Lodash

Our environment comes preloaded with lodash. If you're not familiar with lodash, you can read the documentation for our version.

Generally, it provides you with a lot of easy methods that you wish javascript had, but that it doesn't.

Event Get

var event = Octave.Event.get("s5b7c2258c4eaa25486be2ed1","e5b7c2f8683ee686657c50aac");
// event  now contains the matching event
// event == {elems: { x:1 },creationDate: 1534865286261,...}

Octave.Event.get(streamId, eventId)

Arguments

Return

Returns a json representation of the specified Event.

Throws

Throws an instance of Octave.Error

Event Find

//find the most recent 2 events where x > 1:
var events = Octave.Event.find("s5b7c2258c4eaa25486be2ed1", {"filter": "x > 1", "sort":"creationDate", "limit": 2});
// events  now contains an array with matching values
// events == [{elems: { x:1 },creationDate: 1534865286261,...}, {elems: {x:1}, .... }]

Octave.Event.find(streamId, options)

Arguments

Return

Returns an array of json representations of the matching Events

Throws

Throws an instance of Octave.Error

Event FindOne

//find the most recent events where x > 1:
var event = Octave.Event.findOne("s5b7c2258c4eaa25486be2ed1", {"filter": "x > 1", "sort":"creationDate"});
// event  now contains the matching event
// event == {elems: { x:1 },creationDate: 1534865286261,...}

Octave.Event.findOne(streamId, options)

Takes the same parameters as Octave.Event.find, but returns only first result. Value returned as json representation of an Event.

Arguments

Return

Returns a json representation of a single Event.

Throws

Throws an instance of Octave.Error

Event FindHash

//find the most recent events where x > 1:
var event = Octave.Event.findHash("s5b7c2258c4eaa25486be2ed1", "someUniqueHashValue");
// event  now contains the matching event
// event == {elems: { x:1 },hash:"someUniqueHashValue", creationDate: 1534865286261,...}

Octave.Event.findHash(streamId, fhash)

Function allows for referencing a particular Event by a unique hash value. See hash documentation for further detail.

Arguments

Return

Returns a json representation of a single Event.

Throws

Throws an instance of Octave.Error

Event MultiFind

//query from a number of streams.
var events = Octave.Event.multifind([{streamId:"s123456123456123456123456","params":{"sort":"creationDate","limit":1}},
                                                       streamId:"s234567234567234567234567","params":{"sort":"creationDate","order":"desc", "limit":1}},
                                                       path:/mycompany/mystream", "params":{}]);

Octave.Event.find(streamId, options)

Function allows simulatenous access to Events from a number of streams.

Arguments

Throws

Throws an instance of Octave.Error

Event Aggregate


var myAggregationResults = Octave.Event.aggregate(myStreamId, {
"filter": "EXISTS cpu_temp",
"rules": {"x":"cpu_temp > 50"},
"groupBy": ["$month"],
"output": [
"$avg:cpu_temp",
"$min:cpu_temp",
"$max:cpu_temp",
"$avg:x",
"$count"
],
"sorts":["$avg:x:desc", "$avg:cpu_temp:asc"]
});

Octave.Event.aggregate(streamId, options)

Usage of aggregate is explained at length here.

Stream Find

Octave.Stream.find(options)

Arguments

options: an optional object literal of query options

Return

Returns an array of json representations of the matching Streams.

Throws

Throws an instance of Octave.Error

Cloud Action Find

Octave.Action.find(options)

Arguments

options: an optional object literal of query options

Return

Returns an array of json representations of the matching Actions

Throws

Throws an instance of Octave.Error

Stream Get

Octave.Stream.get(streamId)

Arguments

Return

Returns the json representation of the Stream

Throws

Throws an instance of Octave.Error

Cloud Action Get

Octave.Action.get(cloudActionId)

Arguments

Return

Returns the json representation of the Cloud Action

Throws

Throws an instance of Octave.Error

HTTP GET (webhook)

var getHeaders = {'header1':'1','header2':'2'};
var result = Octave.Http.get('http://httpbin.org/get', getHeaders);

Octave.Http.get(url, headers)

Arguments

Return

Returns a response object with the following keys:

Throws

Throws an instance of Octave.Error

HTTP POST (webhook)

var url = 'http://httpbin.org/post';
var postBody = JSON.stringify(payload);
var postHeaders = {
'Content-Type': 'application/json',
'header2': '2'
};
var result = Octave.Http.post(url, postHeaders, postBody);

Octave.Http.post(url, headers, data)

Arguments

Return

Returns a response object with the following keys:

Throws

Throws an instance of Octave.Error

HTTP PUT (webhook)

var url = 'http://httpbin.org/put';
var putBody = JSON.stringify(payload);
var putHeaders = {
'Content-Type': 'application/json',
'header2': '2'
};
var result = Octave.Http.put(url, putHeaders, putBody);

Octave.Http.put(url, headers, data)

Arguments

Return

Returns a response object with the following keys:

Throws

Throws an instance of Octave.Error

HTTP DELETE (webhook)

var url = 'http://httpbin.org/delete'
var deleteHeaders = {'header1':'1','header2':'2'};
var result = Octave.Http.delete(url, deleteHeaders);

Octave.Http.delete(url, headers)

Arguments

Return

Returns a response object with the following keys:

Throws

Throws an instance of Octave.Error

Edge Action Object

{
  "description": "Transform temperature",
  "source": "observation://temperature_sensor",
  "js": "function(input) {
    var celcius = input.value;
    var fahrenheit = celcius * 9 / 5 + 32;

    var result = {
        c : celcius,
        f : fahrenheit
    };

    return {
      'dh://lcd/txt1' : ['Temp is ' + fahrenheit],
      'cl://' : [result]
    };
    }"
}

An Edge Action specifies a source Observation name and js function.

Input

{
    "value" : {
        "lat" : 40.704067,
        "lon" : -73.989088,
        "alt" : 1,
        "vAcc" : 10,
        "hAcc" : 11,
        "fixType" : "GNSS"
    },
    "timestamp" : 1530572947298
}

The input to an Edge Action is an Event generated by an Observation. Observations are attached to Resources in the Resource Tree. So an Observation on /position/coordinates/value will generate an Event containing the GNSS location of the Device in the value attribute, and the timestamp the reading was taken in the timestamp attribute:

Input Types

"/location/coordinates/value": {
    "dt": "json",
    "s": "{\"lat\":0.1,\"lon\":0.2,\"alt\":0.3,\"hAcc\":0.4,\"vAcc\":0.5,\"fixType\":\"GNSS\"}",
    "t": "input",
    "m": false
}

The value generated from /position/coordinates/value is a JS Object type. We know this by looking in the Device.summary for the same key:

The attribute dt can be one of trigger, boolean, numeric, string, json.

The attribute s shows a "sample" value, so we know what to expect as an input.

Creating an Edge Action

curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/my_company/local-action" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
     "source": "observation://temperature_sensor",
     "js": "function(input) {
       var celcius = input.value;
       var fahrenheit = celcius * 9 / 5 + 32;

       var result = {
         c : celcius,
         f : fahrenheit
       };

       return {
         \\"dh://lcd/txt1\\" : [\\"Temp is \\" + fahrenheit],
         \\"cl://\\" : [result]
       };
     }"
}'
{
  "head": {
    "status": 201,
    "ok": true,
    "messages": [
      "Your request has been processed successfully. A new resource has been created."
    ],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "l5b86acd38c633a1add9e427e",
    "companyId": "c000000000000000000000000",
    "creationDate": 1535552723513,
    "creatorId": "i000000000000000000000000",
    "js": "function(input) {\n       var celcius = input.value;\n       var fahrenheit = celcius * 9 / 5 + 32;\n\n       var result = {\n         c : celcius,\n         f : fahrenheit\n       };\n\n       return {\n         \"dh://lcd/txt1\" : [\"Temp is \" + fahrenheit],\n         \"cl://\" : [result]\n       };\n     }",
    "source": "observation://temperature_sensor",
    "version": 1
  }
}

HTTP Request

POST https://octave-api.sierrawireless.io/v5.0/<company_name>/local-action

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Request JSON Object

Key Type Description
source string Required. The name of the Observation which triggers execution of the Edge Action. Always prefixed with observation://
js string Required. The function must take an Event as input and return a map of destinations to list of Events.

Reading an Edge Action

## Read Device
curl "https://octave-api.sierrawireless.io/v5.0/my_company/local-action/l5b730d2f6f3861bb4e95f51c" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "l5b86acd38c633a1add9e427e",
    "companyId": "c000000000000000000000000",
    "creationDate": 1535552723513,
    "creatorId": "i000000000000000000000000",
    "js": "function(input) {\n       var celcius = input.value;\n       var fahrenheit = celcius * 9 / 5 + 32;\n\n       var result = {\n         c : celcius,\n         f : fahrenheit\n       };\n\n       return {\n         \"dh://lcd/txt1\" : [\"Temp is \" + fahrenheit],\n         \"cl://\" : [result]\n       };\n     }",
    "source": "observation://temperature_sensor",
    "version": 1
  }
}

HTTP Request

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/local-action/<action_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Updating an Edge Action

curl -X "PUT" "https://octave-api.sierrawireless.io/v5.0/my_company/local-action/l5b81acd31c613a1add9e427e" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
     "js": "function(input) {
       var celcius = input.value;
       var fahrenheit = celcius * 9 / 5 + 32;

       var result = {
         c : celcius,
         f : fahrenheit
       };

       return {
         \\"dh://lcd/txt1\\" : [\\"Temp is \\" + fahrenheit],
         \\"dh://lcd/txt2\\" : [\\"Celcius is \\" + celcius],
         \\"cl://\\" : [result]
       };
     }"
}'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "l5b81acd31c613a1add9e427e",
    "companyId": "c000000000000000000000000",
    "creationDate": 1535552723513,
    "creatorId": "i000000000000000000000000",
    "js": "function(input) {\n       var celcius = input.value;\n       var fahrenheit = celcius * 9 / 5 + 32;\n\n       var result = {\n         c : celcius,\n         f : fahrenheit\n       };\n\n       return {\n         \"dh://lcd/txt1\" : [\"Temp is \" + fahrenheit],\n         \"dh://lcd/txt2\" : [\"Celcius is \" + celcius],\n         \"cl://\" : [result]\n       };\n     }",
    "lastEditDate": 1535560013994,
    "source": "observation://temperature_sensor",
    "version": 2
  }
}

HTTP Request

PUT https://octave-api.sierrawireless.io/v5.0/<company_name>/local-action/<action_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Request JSON Object

For a full overview of all Edge Action attributes, see the Edge Action Object Overview.

Key Type Value
js string Required. The function must take an Event as input and return a map of destinations to list of Events.

Deleting an Edge Action

curl -X "DELETE" "https://octave-api.sierrawireless.io/v5.0/my_company/local-action/l5b730d2f6f3861bb4e95f51c" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [
      "Your request has been processed successfully. The requested resource has been deleted."
    ],
    "errors": [],
    "references": {}
  },
  "body": {}
}

HTTP Request

DELETE https://octave-api.sierrawireless.io/v5.0/<company_name>/local-action/<action_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Simulating an Edge Action

curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/my_company/local-action/simulate" \
     -H 'x-auth-user: <user> \
     -H 'x-auth-token: <token> \
     -d $'{
  "event": {
    "value": {
      "x": 1,
      "y": 2,
      "z": 3
    },
    "timestamp": 1532546501162
  },
  "resources": {
    "read://redSensor/light/value": {
      "value": 1100,
      "timestamp": 999999
    },
    "read://redSensor/accel/value": {
      "value": {
        "x": 4,
        "y": 5,
        "z": 6
      }
    },
    "stddev(3.1)://my_temperature_observation": {
      "value": 12.2
    }
  },
  "js": "function(input) {


                 // Read our first mock:

                 // Returns : { value : 1100, timestamp: <current time in millis> }

                 var light = Datahub.read(\\"/redSensor/light/value\\", 0);



                 // Read our second mock:

                 // Returns : { value : {\\"x\\" : 4, \\"y\\" : 5, \\"z\\" : 6}, timestamp: <current time in millis> }

                 var accel = Datahub.read(\\"/redSensor/accel/value\\", 2.0);

                 // If we don'"'"'t supply a mock, we return null immediately:

                 // Returns : null

                 var position = Datahub.read(\\"/redSensor/position/value\\", 1.0);

                 // Return our third mock

                 // Returns : { value : 12.2, timestamp: <current time in millis> }

                 var sdev_temp = Datahub.query(\\"my_temperature_observation\\", \\"stddev\\", 3.1);

                 return {

                   // Direct to Data Hub (path mandatory)

                    \\"dh://lcd/txt1\\" : [\\"hi mom!\\"],

                   // Direct to Cloud (path optional)

                   \\"cl://\\" : [

                       {

                         input : input,

                         light : light,

                         accel : accel,

                         position : '"'"'value is : '"'"' + position,

                         sdev_temp : sdev_temp,

                       }

                   ],

                   // Store and Forward (path optional)

                   \\"st://\\" : [222222],

                   // Set a virtual resource (a single key, mandatory)

                   \\"vr://kjl\\" : [33333],

                   // Debug (no path)

                   \\"debug://\\" : [4444]

               }

    }"
}'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "events": {
      "debug://": [
        4444
      ],
      "st://": [
        222222
      ],
      "cl://": [
        {
          "input": {
            "value": {
              "x": 1,
              "y": 2,
              "z": 3
            },
            "timestamp": 1532546501162
          },
          "light": {
            "value": 1100,
            "timestamp": 999999
          },
          "position": "value is : null",
          "accel": {
            "value": {
              "x": 4,
              "y": 5,
              "z": 6
            },
            "timestamp": 1545249013757
          }
        }
      ],
      "dh://lcd/txt1": [
        "hi mom!"
      ],
      "vr://kjl": [
        33333
      ]
    },
    "log": [
      {
        "input": {
          "value": {
            "x": 1,
            "y": 2,
            "z": 3
          },
          "timestamp": 1.532546501162E12
        },
        "light": {
          "value": 1100,
          "timestamp": 999999
        },
        "Datahub": {},
        "line_no": 9
      },
      {
        "line_no": 17,
        "accel": {
          "value": {
            "x": 4,
            "y": 5,
            "z": 6
          },
          "timestamp": 1.545249013757E12
        }
      },
      {
        "line_no": 29
      }
    ],
    "mocks": null,
    "errors": []
  }
}

This endpoint can be called on either an existing or non-existing Edge Action. If being called on a non-existing Edge Action, the js field must be provided. The endpoint evaluates Edge Action JavaScript against a given Event. (Note: unlike the Event used in Cloud Actions, this Event does not wrap its values in an elems map.) The simulator output will contain the function return value (a map of destinations to values), as well as a log, which is a list of the variables of your running script on each line number in the order they were executed (variables that are unchanged will not be reprinted).

HTTP Request

POST https://octave-api.sierrawireless.io/v5.0/<company_name>/local-action/simulate

POST https://octave-api.sierrawireless.io/v5.0/<company_name>/local-action/<edge_action_id>/simulate

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

request json object

key type description
js string optional - see note above. function to execute
event map required. event to pass to function
resources map optional. used for mocking responses from calls to Datahub.read(), Datahub.query(), etc.

Group Object

Creating a Group

curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/my_company/group" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
  "displayName": "My Group",
  "memberIds": [
    "i000000000000000000000065"
  ],
  "description": "My Group'"'"'s description"
}'
{
  "head": {
    "status": 201,
    "ok": true,
    "messages": [
      "Your request ... has been created."
    ],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "g5b71d2db6f38616bd1ae41f3",
    "companyId": "c000000000000000000000006",
    "creationDate": 1534186203872,
    "creatorId": "i000000000000000000000064",
    "description": "My Group's description",
    "displayName": "My Group",
    "memberIds": [
      "i000000000000000000000065"
    ]
  }
}

Groups are used to combine multiple identities into a single object for purposes of issuing a share. All identities must be members of the company as the company that owns the group.

HTTP Request

POST https://octave-api.sierrawireless.io/v5.0/<company_name>/group

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Query Parameters

Parameter Default Description
refs 0 0 or 1, to disable/enable return of objects referenced by the created object

Request JSON Object

Key Type Description
memberIds array List of identity ids
displayName string Succint name
description string Description

Reading a Group

curl "https://octave-api.sierrawireless.io/v5.0/my_company/group/g5b71d2db6f38616bd1ae41f3" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
  "displayName": "My Group",
  "memberIds": [
    "i000000000000000000000065"
  ],
  "description": "My Group'"'"'s description"
}'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "g5b71d2db6f38616bd1ae41f3",
    "companyId": "c000000000000000000000006",
    "creationDate": 1534186646683,
    "creatorId": "i000000000000000000000064",
    "description": "My Group's description",
    "displayName": "My Group",
    "memberIds": [
      "i000000000000000000000065"
    ]
  }
}

HTTP Request

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/group/<group_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Query Parameters

Parameter Default Description
refs 0 0 or 1, to disable/enable return of objects referenced by the created object

Updating a Group

curl -X "PUT" "https://octave-api.sierrawireless.io/v5.0/my_company/group/g5b71d2db6f38616bd1ae41f3" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
  "memberIds": [
    "i000000000000000000000065",
    "i000000000000000000000064"
  ]
}'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "g5b71d2db6f38616bd1ae41f3",
    "companyId": "c000000000000000000000006",
    "creationDate": 1534186203872,
    "creatorId": "i000000000000000000000064",
    "description": "My Group's description",
    "displayName": "My Group",
    "lastEditDate": 1534186394487,
    "memberIds": [
      "i000000000000000000000065",
      "i000000000000000000000064"
    ]
  }
}

HTTP Request

PUT https://octave-api.sierrawireless.io/v5.0/<company_name>/group/<group_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Query Parameters

Parameter Default Description
refs 0 0 or 1, to disable/enable return of objects referenced by the created object

Deleting a Group

curl -X "DELETE" "https://octave-api.sierrawireless.io/v5.0/my_company/group/g5b71d2db6f38616bd1ae41f3" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [
      "Your request has been processed successfully. The requested resource has been deleted."
    ],
    "errors": [],
    "references": {}
  },
  "body": {}
}

HTTP Request

DELETE https://octave-api.sierrawireless.io/v5.0/<company_name>/group/<group_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Share Object

Creating a Share

curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/my_company/share" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
  "paths": {
    "/my_company/devices": {
      "eventWrite": true,
      "write": true,
      "read": true,
      "eventRead": true
    }
  },
  "roles": [
    "CLOUD_ACTION_READ",
    "CLOUD_ACTION_WRITE",
    "CLOUD_ACTION_DELETE"
  ],
  "issuedTo": "i000000000000000000000065"
}'
{
  "head": {
    "status": 201,
    "ok": true,
    "messages": [
      "Your request has been processed successfully. A new resource has been created."
    ],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "h5b719e696f386161e7eda042",
    "companyId": "c000000000000000000000006",
    "creationDate": 1534172777742,
    "creatorId": "i000000000000000000000064",
    "issuedBy": "i000000000000000000000064",
    "issuedTo": "i000000000000000000000065",
    "paths": {
      "/my_company/devices": {
        "read": true,
        "write": true,
        "eventRead": true,
        "eventWrite": true
      }
    },
    "roles": [
      "CLOUD_ACTION_READ",
      "CLOUD_ACTION_WRITE",
      "CLOUD_ACTION_DELETE"
    ]
  }
}

You can create a share to extend to a another user within the same company any permissions that your identity has.

HTTP Request

POST https://octave-api.sierrawireless.io/v5.0/<company_name>/share

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Query Parameters

Parameter Default Description
refs 0 0 or 1, to disable/enable return of objects referenced by the created object

Request JSON Object

Key Type Description
issuedTo string Required. ID of the recipient of the Share. May be a Group or an Identity. Must be a member of the same company.
paths object Required. Specifies the path-based permissions to grant.
roles array Optional; defaults to empty. Specifies the roles to grant.
description string Optional. Human-friendly description
duration int Optional. In milliseconds.

Available path-based permissions

Permission Description
read Read objects within the given hierarchy
write Write objects within the given hierarchy
eventRead Read events within the given hierarchy
eventWrite Write events within the given hierarchy
administer Shorthand for having all above flags

Available roles

Role Description
CLOUD_ACTION_READ Create cloud action
CLOUD_ACTION_WRITE Edit cloud action
CLOUD_ACTION_DELETE Delete cloud action
LOCAL_ACTION_READ Create local action
LOCAL_ACTION_WRITE Edit local action
LOCAL_ACTION_DELETE Delete local action
BLUEPRINT_READ Create
BLUEPRINT_WRITE Edit
BLUEPRINT_DELETE Delete
TASK_READ Create
TASK_WRITE Edit
TASK_DELETE Delete
COMPANY_WRITE Edit company object
COMPANY_DELETE Permanently delete company and all related resources
COMPANY_MEMBERSHIP_EDIT Reserved for future development

Reading a Share

curl "https://octave-api.sierrawireless.io/v5.0/my_company/share/h5b71a4f66f38616bd1ae41db" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "h5b71a4f66f38616bd1ae41db",
    "companyId": "c000000000000000000000006",
    "creationDate": 1534174454553,
    "creatorId": "i000000000000000000000064",
    "issuedBy": "i000000000000000000000064",
    "issuedTo": "i000000000000000000000065",
    "paths": {
      "/my_company/devices": {
        "read": true,
        "write": true,
        "eventRead": true,
        "eventWrite": true
      }
    },
    "roles": [
      "CLOUD_ACTION_READ",
      "CLOUD_ACTION_WRITE",
      "CLOUD_ACTION_DELETE"
    ]
  }
}

HTTP Request

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/share/<share_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Query Parameters

Parameter Default Description
refs 0 0 or 1, to disable/enable return of objects referenced by the created object

Updating a Share

Deleting a Share

curl -X "DELETE" "https://octave-api.sierrawireless.io/v5.0/my_company/share/h5b719e696f386161e7eda042" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [
      "Your request has been processed successfully. The requested resource has been deleted."
    ],
    "errors": [],
    "references": {}
  },
  "body": {}
}

HTTP Request

DELETE https://octave-api.sierrawireless.io/v5.0/<company_name>/share/<share_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Token Object

Creating a Token

curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/my_company/token" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
  "paths": {
    "/my_company/devices": {
      "eventWrite": true,
      "eventRead": true
    }
  },
  "duration": "60000000"
}'
{
  "head": {
    "status": 201,
    "ok": true,
    "messages": [
      "Your request has been processed successfully. A new resource has been created."
    ],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "k5b71eec26f38616bd1ae4214",
    "companyId": "c000000000000000000000006",
    "creationDate": 1534193346844,
    "creatorId": "i000000000000000000000064",
    "duration": 60000000,
    "paths": {
      "/my_company/devices": {
        "eventRead": true,
        "eventWrite": true
      }
    },
    "tokenString": "uLFm4qCIXUKSVAj6SbmKwpk9iOOV",
    "expiresInMs": 59999998
  }
}

Tokens expose a limited set of path-based permissions on an anonymous basis (dissociated from any particular identity). They are particularly suited for event reading and writing by applications and devices.

HTTP Request

POST https://octave-api.sierrawireless.io/v5.0/<company_name>/token

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Request JSON Object

Key Type Description
paths object Required. Specifies the path-based permissions to grant.
description string Optional. Human-friendly description
duration int Optional. In milliseconds.

Available path-based permissions

Permission Description
eventRead Read events within the given hierarchy
eventWrite Write events within the given hierarchy

Reading a Token

curl "https://octave-api.sierrawireless.io/v5.0/my_company/token/k5b71ed846f38616bd1ae420d" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "k5b71eec26f38616bd1ae4214",
    "companyId": "c000000000000000000000006",
    "creationDate": 1534193346844,
    "creatorId": "i000000000000000000000064",
    "duration": 60000000,
    "paths": {
      "/my_company/devices": {
        "eventRead": true,
        "eventWrite": true
      }
    },
    "tokenString": "uLFm4qCIXUKSVAj6SbmKwpk9iOOV",
    "expiresInMs": 59992978
  }
}

HTTP Request

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/token/<token_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Updating a Token

Deleting a Token

curl -X "DELETE" "https://octave-api.sierrawireless.io/v5.0/my_company/token/k5b71eec26f38616bd1ae4214" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [
      "Your request has been processed successfully. The requested resource has been deleted."
    ],
    "errors": [],
    "references": {}
  },
  "body": {}
}

HTTP Request

DELETE https://octave-api.sierrawireless.io/v5.0/<company_name>/token/<token_id>

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Location Data

Full Example

{
    "path": "/my_company/my_first_stream",
    "location": {
        "lat": 40.703285,
        "lon": -73.987852,
    },
    "elems": {
        "name": "my location"
    }
}

The location field is optional but, if provided, the lat and lon fields are required. All other fields are optional.

Location Map Fields

lat

Latitude

lon

Longitude

alt

Altitude

vAcc

Vertical accuracy

hAcc

Horizontal accuracy

fixType

For internal use

detail

A map of additional optional data

Querying by Location

Filtering

Filters can be used for explicit object matching, as well as within members of Stream and Cloud Action objects, where they control the types of Events that can enter a Cloud Action or Stream.

Operator List

Comparison Type Syntax
Fixed Value Comparison aMemberName (< or <= or == or > or >= or !=) aValue
Element Value Comparison aMemberName (< or <= or == or > or >= or !=) bMemberName
Modulo aMemberName % X == or != ) Y
Regex aMemberName =~ aJavaRegex
IN aMemberName IN ["value1", "value2", "value3"]
CONTAINS aMemberName CONTAINS ["value1", "value2", "value3"]
WITHIN (distance) location WITHIN 5 MILES OF [40.703987, -73.986759]
WITHIN (bounds) location WITHIN [[0,0],[0,10],[10,10],[10,0]]
EXISTS EXISTS aMemberName
AGE AGE (< or <= or == or > or >=) msSinceCreated (TIMEUNIT) or AGE('fieldname') (< or <= or == or > or >=) msSinceCreated (TIMEUNIT)
MATCHES MATCHES /aJavaRegex/
NOT NOT aFilterRule

Fixed Value Comparison

Matches objects with member values that evaluate to true given the specified comparison to a fixed value:

Find all Events in a given Stream where the element "aNumber" has a value less than or equal to 1:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>?filter=elems.aNumber<=1

Find all Events in a given Stream where the element "shipped" is false:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>?filter=elems.shipped!=true

Find all Streams where the description is equal to "myFirstStream":

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/stream?filter=description=="myFirstStream"

Element Value Comparison

Matches objects with member values that evaluate to true given the specified comparison to the value of another member:

Find all Events where the value of the element "length" equals the value of the element "width":

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>?filter=elems.length<=elems.width

Find all Streams where the value of the description member equals the value of the path member:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event?filter=path==description

Modulo

Matches objects with member values that match the given modulo

Find all Events where the counter is a multiple of 100:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>?filter=elems.counter % 100 == 0

Regex

Matches objects with member values that evaluate to true when compared to the given regex.

Find all Events in the given Stream where the value of the element "title" matches the regex /[cC]lojure/:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>?filter=elems.title=~/[cC]lojure/

Find all Streams where the value of the path starts with /acme/:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/stream?filter=path=~/^\/acme\//

Find child Streams of the Stream whose path is /acme/:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/stream?filter=path=~/^\/acme\/[^\/]*$

Find all Cloud Actions feeding Streams whose path starts with /acme/:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/stream?filter=destination=~/^\/acme\//

IN

Matches objects with member values that equal any item of the provided list.

Find all Events where the value of the "status" element is either "shipped" or "delivered":

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>?filter=elems.status IN ["shipped","delivered"]

Find all Cloud Actions where the value of the "destination" element is either "/acme/myFirstStream" or "/acme/mySecondStream":

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/action/<stream_id>?filter=destination IN ["/acme/myFirstStream","/acme/mySecondStream"]

CONTAINS

Matches objects with a list member who's values contain all items of the provided list.

Find all Events where the value of the "ingredients" element contains "vodka":

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>?filter=elems.ingredients CONTAINS ["vodka"]

Find all Events where the value of the "ingredients" element contains both "vodka" AND "gasoline":

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>?filter=elems.ingredients CONTAINS ["vodka","gasoline"]

WITHIN (by distance)

Matches objects with an element location that lies within the specified distance of the given location.

Find all Events with a location element that lies within 500 miles of 40.703987, -73.986759:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>?filter=location WITHIN 500 MILES OF [40.703987, -73.986759]

Available distance units are MILES, FEET, METERS, KILOMETERS

WITHIN (bounding box)

Matches objects with an element location that lies within the specified bounding box. The provided bounding box must have at least 3 distinct points but more can be provided.

Find all Events with a location element that lies within the bounds of points [40.703901,-73.996716],[40.704709,-73.979468],[40.694882,-73.984347]:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>?filter=location WITHIN 500 MILES OF [[40.703901,-73.996716],[40.704709,-73.979468],[40.694882,-73.984347]]

EXISTS

Matches objects with where the member or Event element exists and is not empty.

Find all Events where the element "title" exists:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>?filter=EXISTS elems.title

Find all Streams where the member "filter" exists:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/stream?filter=EXISTS filter

AGE

Matches objects with a date that evaluates to true when compared with the given specifiers. By default, AGE will compare the Event's creationDate. The AGE() macro allows age queries on other date fields such as lastEditDate.

Find all Events with a creation date in the last 3600000 miliseconds:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>?filter=AGE < 3600000

Find all Cloud Actions which were created over a 2 weeks ago:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/action?filter=AGE < 2 WEEKS

This can also be used to search in other date fields:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/action?filter=AGE("lastEditDate") < 42 HOURS

Timeunits supported are MILLISECONDS | SECONDS | MINUTES | HOURS | DAYS | WEEKS. If no timeunit is appended MILLISECONDS is assumed.

MATCHES

Matches any object where members or Event elements which can reasonably be converted to a string. The MATCHES rule is intended for use when you may not know all of the member names needed for a query. If the member fields are known then one or more combined explicit Regex operators will always be more performant.

Find all Events in the given Stream where the value of any "stringable" field matches the regex /[cC]lojure/:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>?filter=MATCHES /[cC]lojure/

Find all Streams where the value of any "stringable" field matches the regex /[cC]lojure/:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/stream?filter=MATCHES /[cC]lojure/

NOTE: id fields will not be searched, instead use one or more combined regex filters.

NOT

Provides the inverse functionality of any filter rule.

Finds all Events in the given Stream where the value of element "title" does not match [cC]lojure:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>?filter=NOT elems.title =~ /[cC]lojure/

Finds all Streams where the value of "description" member does not match [cC]lojure:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/stream?filter=NOT description =~ /[cC]lojure/

Combining Filters

Filters can be combined using logical &&, logical || and grouped.

Finds all Cloud Actions to a Stream whose path is "/acme/destStream" that contain a filter:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/action?filter=destination=="/acme/destStream" && EXISTS filter

Finds all Events in the given Stream with a "on-hand" element whose value is over 100 and "state" is not "cancelled":

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event/stream_id>?filter=elems.on-hand>100 && NOT elems.state=="canceled"

Finds all Cloud Actions to "/acme/sourceStream" from Streams whose paths start with "/a" or end with "b"

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/action?filter=source=="/acme/sourceStream" && (destination=~/^\/a/ || to=~/b$/)

Paging

Controling the number of results

$ curl -G "https://octave-api.sierrawireless.io/v5.0/my_company/event/<stream_id>" \
>   -H "X-Auth-Token: 2R5xwhmlmb9r6z90XCUYqOu1ScJJuPMr" \
>   --data-urlencode 'limit=2'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": [{
    "id": "d585827455caf784b9ef99ba3",
    "streamId": "f53b1d1600cf27b75148de02e",
    "creationDate": 1482172229772,
    "path": "/my_company/lightSensor",
    "version": 0,
    "elems": {
    "measure": 2
    }
  }, {
    "id": "d585827455caf784b9ef99b9f",
    "streamId": "f53b1d1600cf27b75148de02e",
    "creationDate": 1482172229516,
    "path": "/my_company/lightSensor",
    "version": 0,
    "elems": {
      "measure": 1
    }
  }]
}

Append the URL parameter limit=N, where N is the maximum number of Events, defaulting to 20.

Skipping Results

$ curl -G "https://octave-api.sierrawireless.io/v5.0/my_company/event/<stream_id>" \
>   -H "X-Auth-Token: 2R5xwhmlmb9r6z90XCUYqOu1ScJJuPMr" \
>   --data-urlencode 'start=2'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": [{
    "id": "d585827455caf784b9ef99ba3",
    "streamId": "f53b1d1600cf27b75148de02e",
    "creationDate": 1482172229772,
    "path": "/my_company/lightSensor",
    "version": 0,
    "elems": {
    "measure": 2
    }
  }, {
    "id": "d585827455caf784b9ef99b9f",
    "streamId": "f53b1d1600cf27b75148de02e",
    "creationDate": 1482172229516,
    "path": "/my_company/lightSensor",
    "version": 0,
    "elems": {
      "measure": 1
    }
  }]
}

Append the URL parameter start=N, where N is the starting index, defaulting to 0.

Analyzing Events

Event data within a Stream can be grouped and aggregated in powerful ways.

For example:

Event Aggregate calls can be made through the REST API and within the Cloud Action environment. The example below use the REST interface as a convention. However, the same information can be retrieved by a Cloud Action by using the JavaScript function Octave.Stream.Event.aggregate.

Request Format

POST https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>/aggregate

Request Headers

Name Description
X-Auth-Token Token with appropriate permissions
X-Auth-User Identity name performing action

Request JSON Object

Key Type Value
filter string Optional. For defining the parameters of the search, the filter will only include the Events whose data you're interested in aggregating, see the Filter Language documentation for more details
rule object Optional. You can create rules based on boolean filters, for example, "x": "a > 1", or "x" : "location WITHIN 50 MILES OF [40.5,-73.5]" , (in this case you'd refer to these fields in your groupBy or output as the named key of the field, 'x'. make sure your named fields are unique). The output of a rule will always be 1 for true or 0 for false, so $avg:x will return the % that passed the rule.
groupBy list Required. A list of the fields by which you'd like the results grouped. Results can be grouped by any literal element within a Event, or by a Date Selector, e.g. "$month:creationDate"
output list Required. The fields you want to output in your query. These must make use of the Selectors, e.g. "$avg:temperature"
sorts list Optional. The order in which the results are returned. Multiple fields can be specified. Fields must match those specified in the "groupBy" or "output" attributes.

Selectors

Functions that wrap an Event element

Dates

For any field that is specified as a date, you can use a Date Selector to retrieve part of that date. This can be useful for grouping purposes.

The selectors are:

Example: $month:lastEditDate will return only the month component from the date. Use this to group results by the month they were last edited.

By omitting the element name,creationDate will be automatically chosen. E.g. $year returns the year the Event was created.

Aggregate

Specified an aggregation function applied to a field.

Distance

If you have specified a rule element that utilizes a location, e.g. x : location WITHIN 20 MILES of [123,-321], you can use the distance selector $distance:x to retrieve the distance this Event is from [123,-321].

This can be used to sort or group the results, or as a general output.

Example

curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>/aggregate" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
  "filter":"EXISTS cpu_temp",
  "rules":{"x":"cpu_temp > 50"},
  "groupBy":["$month"],
  "output":["$avg:cpu_temp","$min:cpu_temp","$max:cpu_temp","$avg:x","$count"],
  "sorts":["$avg:x:desc", "$avg:cpu_temp:asc"]
}'
{
  "head" : {
    "status" : 200,
    "ok" : true,
    "messages" : [ {
      "query" : {
         "filter":"EXISTS cpu_temp",
         "rules": {"x":"cpu_temp > 50"},
         "groupBy":["$month"],
         "output":["$avg:cpu_temp","$min:cpu_temp","$max:cpu_temp","$avg:x","$count"],
         "sorts":["$avg:x:desc", "$avg:cpu_temp:asc"]}
    } ],
    "errors" : [ ],
    "references" : { }
  },
  "body" : [
    { "_id": { "month": 2, "year": 2015},
      "$avg:cpu_temp": 49.75333333333331,
      "$avg:x": 0.3333333333333333,
      "$count": 120,
      "$max:cpu_temp": 56.2,
      "$min:cpu_temp": 45.5},
    { "_id": { "month": 1, "year": 2015},
      "$avg:cpu_temp": 49.10580204778171,
      "$avg:x": 0.2832764505119454,
      "$count": 293,
      "$max:cpu_temp": 98.6,
      "$min:cpu_temp": 39.0}
  ]
}

For example, if there was a Stream that contained Events indicating the processor's temperature as a 'cpu_temp' elem, in order to obtain the average, minimum and maximum temperatures on a monthly basis:

Versions

curl "https://octave-api.sierrawireless.io/v5.0/my_company/versions/action/a5b5a5b7d32d67b1856414575" \
     -H 'x-auth-user: <user>' \
     -H 'x-auth-token: <token>
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": [
    {
      "id": "a5b5a5b7d32d67b1856414575",
      "companyId": "c5b280e48c939467de918e7f3",
      "creationDate": 1532648317243,
      "creatorId": "i5b1050dd83ee687bb03fb454",
      "description": "Versions demo",
      "js": "function(input){ return { \"/my_company/output\": [input]} }",
      "source": "/my_company/input",
      "version": 1
    },
    {
      "id": "a5b5a5b7d32d67b1856414575",
      "companyId": "c5b280e48c939467de918e7f3",
      "creationDate": 1532648317243,
      "creatorId": "i5b1050dd83ee687bb03fb454",
      "description": "Rennamed action",
      "js": "function(input){ return { \"/my_company/output\": [input]} }",
      "source": "/my_company/input",
      "lastEditDate": 1533823574532,
      "version": 2
    }
  ]
}

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/versions/<object_type>/<object_id>

Certain domain objects (e.g., Cloud Actions, Edge Actions, Blueprints, etc.) are versioned. When they are updated, the previous instance of the object is not deleted. Instead, a version field on the object is incremented and the previous version of the object is preserved for later retrieval. Versions of an object can be queried using the following REST API endpoints.

Valid object_type are action, local-action, and blueprint.

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/versions/<object_type>/<object_id>/<version_number>

curl "https://octave-api.sierrawireless.io/v5.0/my_company/versions/action/a5b5a5b7d32d67b1856414575/2" \
     -H 'x-auth-user: <user>' \
     -H 'x-auth-token: <token>
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
      "id": "a5b5a5b7d32d67b1856414575",
      "companyId": "c5b280e48c939467de918e7f3",
      "creationDate": 1532648317243,
      "creatorId": "i5b1050dd83ee687bb03fb454",
      "description": "Rennamed action",
      "js": "function(input){ return { \"/my_company/output\": [input]} }",
      "source": "/my_company/input",
      "lastEditDate": 1533823574532,
      "version": 2
  }
}

Websockets

Establishing a WebSockets connection to Octave is a two step process:

  1. Request a session id via authenticated HTTP Post
  2. Use the session id in the WSS url

A quick way to get started with WebSockets is to use an interactive command-line tool. In the following examples we'll use wscat, which is a node.js based tool. You can download and install wscat via npm using the following command:

npm install -g wscat

Requesting a Session ID

$ curl -XPOST https://octave-ws.sierrawireless.io/session \
  -H "X-Auth-User: <user>" \
  -H "X-Auth-Company: <company_name>" \
  -H "X-Auth-Token: <token>"
{
  "head": {
    "ok": true,
    "status": 201,
    "errors": []
  },
  "body": {
    "id": "n2fBC9YeE6g.qsQNPOZfoSRP3brEY_9QWapawcjXl04Y"
  }
}

Send a POST request to https://octave-ws.sierrawireless.io/session to request a session. When using a restricted token, one would omit the X-Auth-User header.

Establishing a WSS Connection

$ wscat -c "wss://octave-ws.sierrawireless.io/session/n2fBC9YeE6g.rQzrm_RDmpaiDKcyAYEwzZnQ9EU6ukN5/ws"

connected (press CTRL+C to quit)
>

We'll take the session id provided above and add that to the wss:// url we use to connect. Note that the session ids provided in the POST request expire if there is not an active connection, so make sure to have wscat ready to connect before you request a session id.

When you see the > propt above, you are connected.

Subscribing to Events in a Stream

{
  "msgId": "my-request",
  "object": "event",
  "type": "subscribe",
  "streamId": "s53b1d1600cf27b75148de0ac"
}

{
  "msgId": "my-path-request",
  "object": "event",
  "type": "subscribe",
  "path": "/my/stream/path"
}

WebSockets is a "push" based transport mechanism, and so you can subscribe to one or many Streams and receive all Events in realtime by issuing a subscription request for an individual Stream. You can subscribe by path or id.

Subscription Acknowledgement

{
  "head": {
    "msgId": "my-path-request",
    "ok": true,
    "errors": [],
    "messages": [ "Your request has been processed successfully." ]
  },
  "body": {}
}

Each subscription request will receive and acknowledgement which echos the user-supplied msgId.

Incoming Events

{
  "type": "message",
  "resource": "s53b1d1600cf27b75148de0ac",
  "value": {
    // <... Event object here ...>
  }
}

When an Event is sent to the specified Stream, it will be delivered in the following message format:

Unsubscribing

{
  "msgId": "my-request",
  "object": "event",
  "type": "unsubscribe",
  "streamId": "s53b1d1600cf27b75148de0ac"
}

To unsubscribe from a Stream, just send the same message with the type "unsubscribe," instead of subscribe.

Notifications

Notifications are generated based upon various actions, and are represented as Events that get written to predefined Streams. When a notification is generated, two notification Events are generated. A redacted notification gets written to the redacted notification location, and the unredacted notification gets written to the unredacted notification location. Redaction is performed because a notification might contain sensitive information that a user without permissions shouldn't be able to view. All users have access to the summary notifications stream; only users with the permissions for the object that generated the notification are able to view the unredacted notification.

Redacted notifications are written to the path /my_company/:summary-inbox. This Stream contains notifications for all objects and stores only the most recent action for each particular object.

Unredacted notifications for objects other than devices are written to the path /my_company/:inbox/<object_type>, where valid object types are blueprint, cloud-action, external, group, local-action, mqtt, share, stream, task, and token.

Unredacted notifications for devices are written to /my_company/devices/<device_name>/:inbox.

Unredacted Notifications

curl "https://octave-api.sierrawireless.io/v5.0/my_company/notifications/device/d5b291fc6ff127c21fdce280b" \
     -H 'x-auth-user: <user>' \
     -H 'x-auth-token: <token>
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": [
    {
      "id": "e5b730c9b83ee68665795b5a2",
      "streamId": "s5b291fc6ff127c21fdce281b",
      "creationDate": 1534266523583,
      "generatedDate": 1534266523583,
      "path": "/my_company/devices/my_device/:inbox",
      "version": 0,
      "elems": {
        "sourceId": "d5b291fc6ff127c21fdce280b",
        "actor": {
          "name": "alice",
          "id": "5b2903f6ff127c1f9d80c601"
        },
        "kind": "NOTIFICATION",
        "action": "UPDATE",
        "details": {
          "lastEditDate": {
            "before": 1534266523580,
            "after": 1534266506338
          },
          "state": {
            "before": {
              "/deviceServices/cellular/signal/enable": true,
              "/redSensor/gyro/enable": true,
              "/redSensor/accel/enable": true,
              "/deviceServices/counter/enable": true,
              "/redSensor/light/period": 10,
              "/cloudInterface/store_forward/period": 600,
              "/redSensor/gyro/period": 200,
              "/deviceServices/cellular/signal/period": 10,
              "/cloudInterface/status_line/enable": false,
              "/redSensor/accel/period": 0.5,
              "/deviceServices/counter/period": 0.2,
              "/cloudInterface/developer_mode/enable": false,
              "/util/counter/period": 0.2
            },
            "after": {
              "/deviceServices/cellular/signal/enable": true,
              "/deviceServices/cellular/signal/period": 10,
              "/redSensor/gyro/enable": true,
              "/redSensor/accel/enable": true,
              "/cloudInterface/status_line/enable": false,
              "/redSensor/accel/period": 0.5,
              "/deviceServices/counter/enable": true,
              "/deviceServices/counter/period": 0.2,
              "/redSensor/light/period": 10,
              "/cloudInterface/developer_mode/enable": false,
              "/redSensor/gyro/period": 200,
              "/util/counter/period": 0.2
            }
          }
        },
        "type": "device"
      }
    }
  ]
}

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/notifications/<object_type>/<object_id>

A user that has permission to the Stream /my_company/:inbox would be able to see all notifications for all objects. By default, only users that have Admin Group access will be able to read from this Path. Update notifications provide a diff of fields that have changed. This diff could contain potentially sensitive information, hence why access to unredacted notifications is restricted by permissions.

Users that do not have access to this Path can view notifications for which they are permissioned by using a special REST API endpoint:

Redacted Notifications

{
 "id" : "e000000009e2d492e73356234",
 "streamId" : "s5b4ce3c79e182f53e4f52e02",
 "creationDate" : 1531765704081,
 "lastEditDate" : 1531765705135,
 "generatedDate" : 1531765705133,
 "path" : "/my_company/:inbox",
 "version" : 0,
 "hash" : "s5b4ce3c89e182f53e4f52e39",
 "elems" : {
 "sourceId" : "s5b4ce3c89e182f53e4f52e39",
 "actor" : {
    "name" : "system",
    "id" : "i000000000000000000000001"
 },
 "kind" : "NOTIFICATION",
 "action" : "UPDATE",
 "type" : "device",
    "full" : {
       "eventId" : "e5b859992e23a21558f31ab7b",
       "streamId" : "s5b3ab093665c545444523500"
    }
 }
}

At right is an example redacted notification. Potentially sensitive information is unavailable. The redacted notification contains a reference to the unredacted notification (the Stream in which it is located and its Event object id).

Errors

The Octave REST API uses the following error codes:

Error Code Meaning
400 Bad Request -- Your request is invalid.
403 Forbidden -- You do not have access to the given object/namespace.
404 Not Found -- The specified resource could not be found.
500 Internal Server Error -- We had a problem with our server. Try again later.