NAV Navbar
shell

Octave API Documentation

Octave is the all-in-one edge-to-cloud solution for connecting your industrial assets. Octave makes it easy to securely extract, orchestrate, and act on data from your assets at the edge to the cloud. If you are new to Octave, start with our Developer Hub at docs.octave.dev then come back here when you are ready for detailed API docs.

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, Actions, Connectors. 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 a 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.

Cloud Connector (Preview)

Cloud Connectors are typed objects, with distinct connector types providing connectivity for specific cloud services. For example, an http-connector is a general purpose connector type for streaming events to an http(s) services. While the overall connector API is uniform and general in nature, each connector type will have its own required and optional properties. These required properties will be noted in type specific documentation for each supported connector type.

We currently support the following connector types:

Common Properties

{
  "type": "http-connector",
  "displayName": "example http connector",
  "description": "basic http connector using x-auth header authorization with custom headers",
  "source": "/my_company/my_cool_project/incoming",
  "filter": "elems.error_rate > 0.1",
  "disabled": false,
  "headers": {
    "x-auth-user": "x-auth-user-value",
    "x-auth-token": "x-auth-token-value",
    "x-custom-0": "x-custom-0-value",
    "x-custom-1": "x-custom-1-value",
    "Content-Type": "applications/json"
  },
  "properties": {
    "success-codes": [201, 202, 203, 204, 205],
    "method": "POST"
  },
  "routingScript": "function(event) { return \"https://my.test.server:77\"; }",
  "js": "function(event) {
    /* any transformation of input event as required */
    return { /* return any json to be exported to the defined route */
      \"your-key-0\": \"your-value-0\",
      \"your-key-n\": \"your-value-n\"
    };
    }"
}

Defining cloud-connectors is similar to defining cloud-actions: we identify a stream source for events to be processed and define a JavaScript function to transform events.

Unlike Cloud-Actions, Cloud Connectors publish their result to specified service end-points. This target service is either statically defined or dynamically specified via an optional routingScript. For example, in the case of general HTTP based connectors, we provide a second JavaScript function in the routingScript property that returns the destination URL and any necessary headers and/or properties per specific connector type, whereas routing information for distinguished external services (such as Azure Iot Hub) is derived from the general connector properties.

Headers typically include authorization information and properties are connector type specific.

We'll use a basic http-connector example to note the general and type specific consideration.

type

Required field. The type of a connector. Supported types are:

description

Optional human-readable description.

displayName

Name used to identify the connector in the UI.

source

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

filter

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

headers

Headers are static user defined message metadata, analogous to the 'envelope' metadata of messaging protocols. Elements of the headers map are connector type specific. In general, any header defined is passed directly to the corresponding end-point, unless specifically noted in connector type specific documentation.

properties

Properties are used by connectors to configure and/or modify their operation. Elements of the properties map are connector type specific. For example, all http protocol based connectors require a provided white-list of status codes indicating a"successful" export of an event, beyond the implicit 200status code. Required properties for each connector type is noted in connector type specific documentation.

js

{
  "js": "function(event) {
    return {
      \"event\":      event,
      \"your-key-0\": your_value_0,
        ...
      \"your-key-n\": your_value_n
    };
}

Required field. A JavaScript function with a single input argument of an Event, return type is a string.

routingScript

Optional field. A JavaScript function with a single input argument of an Event, returning a string representation of a connector type specific end-point address. This may be a static result ignoring the input event, however dynamic routing can be achieved by using data from the input event to compute the route for each sourced event. As of the preview release, the routing script is subject to the same constraints as general action JavaScript functions.

Specific restrictions may apply per specific connector types.

HTTP-Connector

This is a general purpose HTTP based connector (currently only) supporting the (implicit) POST method connectivity to external HTTP endpoints.

The HTTP Connector API refines the above general connector considerations. These refinements are as follows:

type

Type is "http-connector".

routingScript

{
   "routingScript": "function(ignored) { return \"https://my.company.com/my-service\"; }"
}

This is a required field for this type of connector, a JavaScript function returning the string representation of the destinations' URL.

The simplest routingScript function is a function ignoring the input event and always returning the same URL.

While it is possible that entirely distinct end-point URLs can be returned by this function, typically the variation of the URL is the specification of event specific query parameters or distinct services within the same domain.

headers

{
   ...
   "headers": {
      "x-some-header": "header value"
   },
   ...
}

All http-connector definitions must provide a headers map minimally specifying the standard Content-Type header. You may also define any other application specific header. Only static header values are currently supported.

Basic static authentication via headers is also supported as a consequence. Headers and header values defined will be passed directly to the specified end-points.

A minimal header definition for http-connector is shown to the right.

properties

{
   ...
   "properties": {
     "success-codes": [201, 203],
     "method": "POST"
   },
   ...
}

All http-connector definitions must provide a properties map minimally specifying the white list of HTTP status codes to be considered as OK or "successful". 200/OK is implicit.

No other properties are currently supported by the general http-connector type.

A minimal properties definition for http-connector connecting to a service that only replies with 200/OK is as follows:

{
   ...
   "properties": {
      "success-codes": []
   },
   ...
}

Azure IotHub HTTP Gateway Connector

Iot Hub Connector types allow for publishing to Azure Iot Hub via Iot Hub HTTP REST endpoint. This connector supports authentication via Shared Access Policy keys. All non-IotHub specific considerations are per general connectors, as defined above.

type

Type is "azure-iothub-http-gateway-connector".

properties

The following standard Iot Hub properties allow for authenticating the connector with Azure via SAS tokens. See Shared Access Policy in your Azure Iot Hub portal for obtaining these properties.

This connector assumes the default Shared Access Policy named 'device'. You may optionally defined the policy name via an optional property element, as indicated below.

{
   ...
   "properties": {
      "iothub-name": "<required: your iot hub name>",
      "shared-access-key": "<required: your iot hub shared access policy key>",
      "shared-access-policy-name": "<optional: default policy name is 'device'>"
   },
   ...
}

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.

Task

{
  "displayName": "My Task",
  "description": "Periodic task that runs every minute",
  "disabled": false,
  "source": {
    "eventFind": {
      "streamId": "s5e5d6db4772f39d6b996da59",
      "options": {
        "limit": 1
      }
    }
  },
  "js": "function(events, resp) { return {'/my_company/task_output': events}; }",
  "lastRun": 1593696976481,
  "nextRun": 1593697036481,
  "periodicity": 60000,
  "runCt": 17,
  "status": "OK"
}

Tasks are similar to Cloud Actions, but they are executed periodically instead of being triggered by the creation of a Event in a Stream. Hence, a Task enables the periodic execution of transformations on Events (or data from external systems) and storing the results in Streams.

Tasks have a convenience source property that allows specifying how to retrieve the data that will be fed to the js function. Supported sources are eventFind (equivalent to an Octave.Event.find() call), and http (equivalent to an Octave.http.* call plus optional jsonpath, xpath or rss parsing.) See Source object for more details.

Similar to Cloud Actions, a destination can be provided instead of js to have the source Events routed directly to a Stream without transformation.

displayName

Optional human-friendly name.

description

Optional human-readable description.

disabled

Boolean indicating whether the Task is enabled or not, in which case it won't be run.

source

Defines declaratively how data will be ingested into the Task's JavaScript function. If unspecified, the Task JavaScript will be executed with empty input arguments. See Source object for more details.

destination

Must be specified if js is unspecified. Path where processed events will be written to.

js

Must be specified if destination is unspecified. The function must take a list of Events (it will contain the retrieved/parsed events if a source was specified) and a raw reponse object (for http sources, it will contain the raw HTTP response object) as input and return a map of destinations to list of Events.

lastRun

Time of the last Task execution in milliseconds since epoch.

nextRun

Time of the next planned Task execution in milliseconds since epoch.

periodicity

The amount of milliseconds between two succesive task runs. The minimum allowed value is 20000.

runCt

The number of times the Task has been run.

status

State of the last Task execution: OK (successful run), ERROR (there was an error executing the Task) or NO_NEW_DATA (if the events returned from task execution were already contained in the previous execution's results.)

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
    }
  }
}

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.

Firmware

{
  "version": "1.2.3"
}

A Firmware represents a firmware version that available for edge devices. A Firmware object may be either available for all companies (if its attribute companyId is null or not set) or only to a specific to a company (the one indicated by a non-null companyId.)

version

The firmware version.

Release Note

{
  "notes": "# Octave API version 5.3.1....",
  "version": "1.0.0"
}

A Release Note contains information about a version of the Octave API. A Release Note object may be either available for all companies (if its attribute companyId is null or not set) or only to a specific to a company (the one indicated by a non-null companyId.)

version

The related Octave API version

notes

A Markdown text describing of the version's release notes.

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-api.sierrawireless.io/v5.0/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.

Device 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

{
  "metadata" : {
    "ttl" : 60000
  },
  "elems": {
    "/redSensor/light/period" : 10,
    "/lcd/txt1" : "Hello World!"
  }
}
{
  "metadata" : {
    "timeout" : 1598292125
  },
  "elems": {
    "/redSensor/light/period" : 10,
    "/lcd/txt1" : "Hello World!"
  }
}

To send an individual command to a device, such as setting a resource value or configuration, add an Event to the /<company_name>/devices/<device_name>/:command Stream. Within the Event Elements, keys are the Resource Paths, and values are the values to be pushed.

Values are pushed to each Resource referenced in the Command. However, those values will be applied once-only. When the device reboots, the Resources will be set with their original values as defined in the Device.state attribute.

The user can specify a timeout or time-to-live (ttl). Octave will attempt to deliver the Command to the device until it succeeds, or the timeout is exceeded. Timeout and TTL are defined as attributes of the Event.metadata field:

Property Description
timeout Unix Epoch Time (milliseconds). Absolute time, after which we no longer try to deliver the Command
ttl Duration (milliseconds). Relative time, after which we no longer try to deliver the Command

The user may specify one of these attributes, or none (defaults to "no timeout"). If an invalid value is supplied, it is treated as "no timeout". If both attributes are supplied, Octave will use the timeout value.

Command Notifications

{
  "elems": {
    "action": "COMMAND_START",
    "kind": "NOTIFICATION",
    "details" : {
      "eventId" : "e5b5203a790f45d5437c4e35c",
      "streamId": "s5e4c41c5680fcd35eadf0fc1"
    },
    "type": "device"
  }
},
{
  "elems": {
    "action": "COMMAND_COMPLETE",
    "kind": "NOTIFICATION",
    "details" : {
      "eventId" : "e5b5203a790f45d5437c4e35c",
      "streamId": "s5e4c41c5680fcd35eadf0fc1"
    },
    "type": "device"
  }
},
{
  "elems": {
    "action": "COMMAND_FAULT",
    "kind": "NOTIFICATION",
    "details" : {
      "reason": "Command not delivered due to expired timeout",
      "eventId" : "e5b5203a790f45d5437c4e35c",
      "streamId": "s5e4c41c5680fcd35eadf0fc1"
    }
    "type": "device"
  }
}

Notifications are generated in the Device Inbox /<company_name>/devices/<device_name>/:command Stream for the following:

Action Description
Command Start A Command has been created, is awating delivery
Command Finish A Command has been delivered and executed
Command Failed Octave has failed to deliver the Command

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. The 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 has 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

## Create Device
curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/my_company/device/provision" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
     "name": "device name",
     "imei": "352653090106733",
     "fsn": "VU810385240210"
     }'
{
  "head": {
    "status": 201,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "o5df3fd00b03f0746a3bd0dc4",
    "action": "PROVISION",
    "companyId": "c5b5203a790f45d5437c4e35b",
    "complete": false,
    "creationDate": 1576271104719,
    "creatorId": "i5dea9ae317900a4b57f95b39",
    "details": {
        "device_details": {
          "fsn": "VU810385240210",
          "imei": "352653090106733"
        }
    },
    "deviceIds": [
      "d5df3fd00b03f0746a3bd0d9f"
    ],
    "state": "STARTED",
    "status": {},
    "timeout": 86400
  }
}

HTTP Request

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

Request Headers

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

Request JSON Object

Key Type Value
name string Device name
imei string IMEI of the Device
fsn string Serial number of the Device

Listing company Devices

## List Devices
curl "https://octave-api.sierrawireless.io/v5.0/my_company/device" \
     -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

Request Headers

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

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

Moving a Device to another Company

Devices can be moved to other Companies of which the requesting user is a member.

Transforming Device Attributes

When moving a Device between Companies the following tranformations can be specified.

Rules for Moving Devices

curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/my_company/device/transfer" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
    "deviceIds" : ["d5f11ecaf1d8f562eb4821ace", "d5bd06e3ebd1f816495786271"],
    "companyId" : "c5bae617e90f45d7d11710087",
    "transform" : {
      "d5f11ecaf1d8f562eb4821ace" : {
        "name" : "my_new_device_name",
        "blueprintId" : {
          "id" : "b5c37879220721249d5c8ffa3",
          "version" : 3
        },
        "deployFirmware" : false
      }
    }
  }
}'
{
  "head": {
    "status": 201,
    "ok": true,
    "messages": [
      "Your request has been processed successfully. A new resource has been created."
    ],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "o5f189c29dccf28c9f178e576",
    "action": "TRANSFER_DEVICE",
    "companyId": "c5b5203a790f45d5437c4e35b",
    "complete": false,
    "creationDate": 1595448361168,
    "creatorId": "i5adf463709159f4dde43a86f",
    "details": {
      "companyId": "c5bae617e90f45d7d11710087",
      "transform" : {
        "d5f11ecaf1d8f562eb4821ace" : {
          "name" : "my_new_device_name",
          "blueprintId" : {
            "id" : "b5c37879220721249d5c8ffa3",
            "version" : 3
          },
          "deployFirmware" : false
        }
      }
    },
    "deviceIds": [
      "d5f11ecaf1d8f562eb4821ace",
      "d5bd06e3ebd1f816495786271"
    ],
    "lastEditDate": 1595448361168,
    "lastEditorId": "i5adf463709159f4dde43a86f",
    "state": "UNKNOWN",
    "status": {
      "d5f11ecaf1d8f562eb4821ace": {
        "MOVE_DEVICE": {
          "state": "UNKNOWN",
          "ts": 1595448361166
        },
        "PUSH_CONFIGURATION": {
          "state": "UNKNOWN",
          "ts": 1595448361166
        }
      },
      "d5bd06e3ebd1f816495786271": {
        "MOVE_DEVICE": {
          "state": "UNKNOWN",
          "ts": 1595448361166
        },
        "PUSH_CONFIGURATION": {
          "state": "UNKNOWN",
          "ts": 1595448361166
        }
      }
    }
  }
}

HTTP Request

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

Request Headers

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

Request JSON Object

Key Type Value
deviceIds array Required. A list of Device IDs to be moved
companyId string Required. The ID of the Company the Devices shall be moved to
transform map Optional. Options for per-Device transformations
transform.<device-id>.name string Optional. The new name of the Device
transform.<device-id>.blueprintId map Optional. The Blueprint to apply to the Device
transform.<device-id>.deployFirmware bool Optional. Should the firmware attached to the supplied Blueprint be installed? Default = false

Query Parameters

Parameter Default Description
simulate false Validate request and show potential response, but don't perform the transfer

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.

Listing company Streams

## List Streams
curl "https://octave-api.sierrawireless.io/v5.0/my_company/stream" \
     -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

Request Headers

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

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

Bulk Deleting all Events in Stream

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

HTTP Request

DELETE 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 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

Listing company Cloud Actions

curl "https://octave-api.sierrawireless.io/v5.0/my_company/action" \
     -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

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

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 Connector Object (Preview)

Octave currently supports the following HTTP based connector types:

Creating a Cloud Connector

Defining cloud-connectors is similar to defining cloud-actions: we identify a stream source for events to be processed and define a JavaScript function to transform events. We can also, where-required, provide a scripted destination URL (as routingScript), and any necessary headers and/or properties per specific connector type. Headers typically include authorization information and properties are connector type specific.

The key distinctions to keep in mind are that:

Common Fields

Cloud Connector Uniqueness

What logically distinguishes individual cloud-connectors is precisely the connectivity path from a source to a destination. This maps to a connectors source and its routingScript. For a given company (scope) all cloud connectors must be unique.

Creating an 'http-connector'

curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/my_company/connector" \
    -H 'X-Auth-Token: <token>' \
    -H 'X-Auth-User: <user>' \
    -H 'Content-Type: application/json; charset=utf-8' \
    -d $'{
    "type": "http-connector",
    "source": "/my_company/source",
    "disabled": false,
    "displayName": "example http connector",
    "description": "example using custom headers",
    "js": "function(event) { return JSON.stringify({'some-key': 'some-value'}) }",
    "routingScript": "function(event) { return 'https://some.server:1234/service'}",
    "headers": {
        "Content-Type": "application/json",
        "x-custom": "x-custom-value"
    },
    "properties": {
        "success-codes": [201, 202, 203, 204, 205]
    },
{
  "head": {
    "status": 201,
    "ok": true,
    "messages": [
      "Your request has been processed successfully. A new resource has been created."
    ],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "x5e41c286c80a6459ae7aa8a1",
    "companyId": "c5bae617e90f45d7d11710087",
    "creationDate": 1581367942891,
    "creatorId": "i5c2ce278a25a5f099f95a189",
    "description": "example using standard x-auth and custom headers",
    "disabled": false,
    "displayName": "example http connector",
    "headers": {
        "Content-Type": "application/json",
        "x-custom": "x-custom-value"
    },
    "js": "function(event) { return JSON.stringify({'some-key': 'some-value'}) }",
    "properties": {
      "success-codes": [
        201,
        202,
        203,
        204,
        205
      ]
    },
    "routingScript": "function(event) { return 'https://some.server:1234/service'}",
    "source": "/test/foo",
    "type": "http-connector",
    "version": 1
  }
}

Creating an 'azure-iothub-http-gateway-connector'

Iot Hub connectors have no header requirements, adhere to the default response status codes of Azure Iot Hub, and also do not require a routingScript. The key required properties are your Azure Iot Hub application's name, shared access policy key, and an optional shared access policy name.

Note that use of Iot Hub Connectors requires mapping of Azure Iot Hub "device twin" ids to associated Octave device. For each device sending Events to the connector's source path, make sure the Device's metadata contains the key occ-iothub-device-id with the value of the IoT Hub device.

curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/my_company/connector" \
    -H 'X-Auth-Token: <token>' \
    -H 'X-Auth-User: <user>' \
    -H 'Content-Type: application/json; charset=utf-8' \
    -d $'{
    "type": "azure-iothub-http-gateway-connector",
    "source": "/my_company/source",
    "disabled": false,
    "displayName": "example IoT Hub Gateway Connector",
    "description": "Sends events from Octave Devices to their associated device on Azure IoT Hub",
    "js": "function(event) { return event; }",
    "properties": {
        "iothub-name": "<from your Azure Iot Hub Portal>",
        "shared-access-key": "<either the default 'device' key for the IoT Hub account or one with similar permissions>",
        "shared-access-policy-name": "<policy name, only required if not using built-in 'device' key>"
    },

Permission to create a Cloud Connector is governed by the CLOUD_CONNECTOR_WRITE role.

HTTP Request

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

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
disabled bool is action disabled? (default false)
source string required. path from which to read events.
js string required. javascript function definition to create exported payload from event. This will be an a function taking 1 argument, returning an arbitrary JSON map.
properties map required-per-type. Typically there are specific properties per distinct connector types. Properties are not passed along with the exported event -- they are only required for parameterizing the operation of specific connector types.
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

Listing company Cloud Connectors

curl "https://octave-api.sierrawireless.io/v5.0/my_company/connector" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": [
    {
      "id": "x5e41c286c80a6459ae7aa8a1",
      "companyId": "c5bae617e90f45d7d11710087",
      "creationDate": 1581367942891,
      "creatorId": "i5c2ce278a25a5f099f95a189",
      "description": "example using standard x-auth and custom headers",
      "disabled": false,
      "displayName": "example http connector",
      "headers": {
        "x-auth-user": "x-auth-user-value",
        "x-auth-token": "x-auth-token-value",
        "x-custom": "x-custom-value",
        "Content-Type": "applications/json"
      },
      "js": "function(event) { return {\"key-0\": \"value\"}; }",
      "properties": {
        "success-codes": [
          201,
          202,
          203,
          204,
          205
        ]
      },
      "routingScript": "function(event) { return \"https://my.test.server:77\"; }",
      "source": "/test/foo",
      "type": "http-connector",
      "version": 1
    },
    ...
  ]
}

Permission to read Cloud Connectors is governed by the CLOUD_CONNECTOR_READ role.

HTTP Request

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

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

Reading a Cloud Connector

curl "https://octave-api.sierrawireless.io/v5.0/my_company/connector/x5e41c286c80a6459ae7aa8a1" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "x5e41c286c80a6459ae7aa8a1",
    "companyId": "c5bae617e90f45d7d11710087",
    "creationDate": 1581367942891,
    "creatorId": "i5c2ce278a25a5f099f95a189",
    "description": "example using standard x-auth and custom headers",
    "disabled": false,
    "displayName": "example http connector",
    "headers": {
      "x-auth-user": "x-auth-user-value",
      "x-auth-token": "x-auth-token-value",
      "x-custom": "x-custom-value",
      "Content-Type": "applications/json"
    },
    "js": "function(event) { return {\"key-0\": \"value\"}; }",
    "properties": {
      "success-codes": [
        201,
        202,
        203,
        204,
        205
      ]
    },
    "routingScript": "function(event) { return \"https://my.test.server:77\"; }",
    "source": "/test/foo",
    "type": "http-connector",
    "version": 1
  }
}

Permission to read Cloud Connectors is governed by the CLOUD_CONNECTOR_READ role.

HTTP Request

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/connector/<connector_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 Connector

curl -X "PUT" "https://octave-api.sierrawireless.io/v5.0/my_company/connector/a5b7337e76f38615fb24a4ea8" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
  "description": "My Cloud Connector"
}'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "x5e4185cc2f2ea07c486d384c",
    "companyId": "c000000000000000000000006",
    "creationDate": 1581352396959,
    "creatorId": "i000000000000000000000064",
    "description": "example using standard x-auth and custom headers",
    "disabled": true,
    "displayName": "My cloud connector",
    "headers": {
      "x-auth-user": "x-auth-user-value",
      "x-auth-token": "x-auth-token-value",
      "x-custom": "x-custom-value",
      "Content-Type": "applications/json"
    },
    "js": "function(event) { return {\"key-0\": \"value\"}; }",
    "lastEditDate": 1581353594708,
    "lastEditorId": "i000000000000000000000064",
    "properties": {
      "success-codes": [
        201,
        202,
        203,
        204,
        205
      ]
    },
    "routingScript": "function(event) { return \"https://localhost:7777\"; }",
    "source": "/test",
    "type": "http-connector",
    "version": 2
  }
}

Permission to update a Cloud Connector is governed by the CLOUD_CONNECTOR_WRITE role.

HTTP Request

PUT https://octave-api.sierrawireless.io/v5.0/<company_name>/connector/<connector_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
disabled bool is action disabled?
source string required. path from which to read events.
js string required. javascript function definition to create exported payload from event. This will be an a function taking 1 argument, returning an arbitrary JSON map.
routingScript string optional. javascript function definition to create a URL string representation from the event. This will be a function taking 1 arument, returning a string.
headers string required-per-type. header passed verbatim to the indicated external end-point via the connector. Typically there are specific headers per distinct connector types. Any additional custom headers not required by the connector-type is also passed verbatim.
properties map required-per-type. Typically there are specific properties per distinct connector types. Properties are not passed along with the exported event -- they are only required for parameterizing the operation of specific connector types.
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 Connector

curl -X "DELETE" "https://octave-api.sierrawireless.io/v5.0/my_company/connector/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_CONNECTOR_DELETE role.

HTTP Request

DELETE https://octave-api.sierrawireless.io/v5.0/<company_name>/connector/<connector_id>

Request Headers

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

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 Action and Task execution environments. These methods are available in the global namespace 'Octave'. The 'Octave' namespace provides 'find' and 'get' under the Stream subnamespace. The Event subnamespace includes 'get', 'find', 'findOne', 'findHash', 'multiFind', and 'aggregate' methods. Lastly, the Device subnamespace provides the methods 'getName', 'get', 'find', and 'update'.

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.

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

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.

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, hashValue)

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.

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.

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.

Stream Get

Octave.Stream.get(streamId)

Arguments

Return

Returns the json representation of the Stream

Device Get Name

Octave.Device.getName()

Arguments

Return

Returns the name of the Device associated with the Event that triggered the Cloud Action. Returns null if called outside of the Cloud Action javascript context.

Device Get

Octave.Device.get(deviceId)

Arguments

Return

Returns the json representation of the Device

Device Find

Octave.Device.find(options)

Arguments

options: an optional object literal of query options

Return

Returns an array of json representations of the matching Devices.

Device Update

Octave.Device.update(deviceId, deviceDiff)

Return

Returns the json representation of the updated Device

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:

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:

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:

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:

Edge Action Object

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

    var result = {
        c : celsius,
        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 celsius = input.value;
       var fahrenheit = celsius * 9 / 5 + 32;

       var result = {
         c : celsius,
         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 celsius = input.value;\n       var fahrenheit = celsius * 9 / 5 + 32;\n\n       var result = {\n         c : celsius,\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.

Listing company Edge Actions

## List Edge Actions
curl "https://octave-api.sierrawireless.io/v5.0/my_company/local-action" \
     -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 celsius = input.value;\n       var fahrenheit = celsius * 9 / 5 + 32;\n\n       var result = {\n         c : celsius,\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

Request Headers

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

Reading an Edge Action

## Read Edge Action
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 celsius = input.value;\n       var fahrenheit = celsius * 9 / 5 + 32;\n\n       var result = {\n         c : celsius,\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 celsius = input.value;
       var fahrenheit = celsius * 9 / 5 + 32;

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

       return {
         \\"dh://lcd/txt1\\" : [\\"Temp is \\" + fahrenheit],
         \\"dh://lcd/txt2\\" : [\\"Celsius is \\" + celsius],
         \\"cl://\\" : [result]
       };
     }"
}'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "l5b81acd31c613a1add9e427e",
    "companyId": "c000000000000000000000000",
    "creationDate": 1535552723513,
    "creatorId": "i000000000000000000000000",
    "js": "function(input) {\n       var celsius = input.value;\n       var fahrenheit = celsius * 9 / 5 + 32;\n\n       var result = {\n         c : celsius,\n         f : fahrenheit\n       };\n\n       return {\n         \"dh://lcd/txt1\" : [\"Temp is \" + fahrenheit],\n         \"dh://lcd/txt2\" : [\"Celsius is \" + celsius],\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.

Task Object

Creating a Task

## Create Task
## Task #1: Find all devices and add them as event elements on a destination stream
curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/my_company/task" \
    -H 'X-Auth-Token: <token>' \
    -H 'X-Auth-User: <user>' \
    -H 'Content-Type: application/json; charset=utf-8' \
    -d $'{
    "destination": "/my_company/task_output/find_devices",
    "periodicity": "20000",
    "js": "function(events, raw) { var devices = 'EMPTY'; try { devices = Octave.Device.find({});} catch (e) { devices = e.name + ' ' + e.message;} return {'/my_company/task_output/find_devices': [{'elems': {'devices': devices}}]}; }",
    "displayName": "A sample task-1"
}'
## Response for Task #1
{
    "head": {
        "status": 201,
        "ok": true,
        "messages": [
            "Your request has been processed successfully. A new resource has been created."
        ],
        "errors": [],
        "references": {}
    },
    "body": {
        "id": "t5e5d3e8d772f39d6b996d143",
        "companyId": "c5bae617e90f45d7d11710087",
        "creationDate": 1583169165597,
        "creatorId": "i5dea9ae317900a4b57f95b39",
        "destination": "/my_company/task_output/find_devices",
        "displayName": "A sample task-1",
        "js": "function(events, raw) { var devices = 'EMPTY'; try { devices = Octave.Device.find({});} catch (e) { devices = e.name + ' ' + e.message;} return {'/my_company/task_output/find_devices': [{'elems': {'devices': devices}}]}; }",
        "lastRun": 0,
        "nextRun": 0,
        "periodicity": 20000,
        "runCt": 0,
        "status": "OK"
    }
}
## Task #2: Generate one event every hour and use eventFind
curl -X "POST" \ "https://octave-api.sierrawireless.io/v5.0/my_company/task" \
    -H 'X-Auth-Token: <token>' \
    -H 'X-Auth-User: <user>' \
    -H 'Content-Type: application/json; charset=utf-8' \
    -d $'{
      "destination": "/my_company/task_output/everyday",
      "displayName": "Generate one event every hour",
      "js": "function(events, raw) {var ev = {elems: {}}; if (events && events[0] != null) { ev.elems.hour = (events[0].elems.hour + 1) % 24; } else { ev.elems.hour = 1; } return {'/my_company/test/periodictasks/everyday': [event] }}",
      "periodicity": 3600000,
      "source": {
        "eventFind": {
          "streamPath": "/my_company/task_output/everyday",
          "options": {
            "limit": 1
          }
        }
      }
    }'
## Response for Task #2
{
  "head": {
    "status": 201,
    "ok": true,
    "messages": [
      "Your request has been processed successfully. A new resource has been created."
    ],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "t5eff44ec14c322021cd812be",
    "companyId": "c000000000000000000000006",
    "creationDate": 1593787628423,
    "creatorId": "i000000000000000000000064",
    "destination": "/my_company/task_output/everyday",
    "displayName": "Generate one event every hour",
    "js": "function(events, raw) {var ev = {elems: {}}; if (events && events[0]) { ev.elems.hour = (events[0].elems.hour + 1) % 24; } else { ev.elems.hour = 1; } return {'/my_company/task_output/everyday': [ev] }}",
    "lastEditDate": 1593787628423,
    "lastEditorId": "i000000000000000000000064",
    "lastRun": 0,
    "nextRun": 0,
    "periodicity": 3600000,
    "runCt": 0,
    "source": {
      "eventFind": {
        "streamPath": "/my_company/task_output/everyday",
        "options": {
          "limit": 1
        }
      }
    },
    "status": "OK"
  }
}
## Task #3: Reading JSON HTTP response
curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/my_company/task" \
    -H 'X-Auth-Token: <token>' \
    -H 'X-Auth-User: <user>' \
    -H 'Content-Type: application/json; charset=utf-8' \
    -d $'{
      "destination": "/my_company/task_output/task3",
      "displayName": "Task with HTTP jsonpath parser source",
      "periodicity": 20000,
    "source": {
      "http": {
          "parser": {
            "jsonpath": {
              "nodes": {
                "elems": {
                  "headers": "headers.Accept",
                  "a": "args.a"
                }
        },
              "root": "$"
      }
    },
          "get": {
            "url": "http://httpbin.org/get?a=b"
          }
        }
      }
}'
## Response for Task #3
{
  "head": {
    "status": 201,
    "ok": true,
    "messages": [
      "Your request has been processed successfully. A new resource has been created."
    ],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "t5eff495a14c322021cd812cd",
    "companyId": "c000000000000000000000006",
    "creationDate": 1593788762600,
    "creatorId": "i000000000000000000000064",
    "destination": "/my_company/task_output/task3",
    "displayName": "Task with HTTP jsonpath parser source",
    "lastEditDate": 1593788762600,
    "lastEditorId": "i000000000000000000000064",
    "lastRun": 0,
    "nextRun": 0,
    "periodicity": 20000,
    "runCt": 0,
    "source": {
      "http": {
        "parser": {
          "jsonpath": {
            "nodes": {
              "elems": {
                "headers": "headers.Accept",
                "a": "args.a"
              }
            },
            "root": "$"
          }
        },
        "get": {
          "url": "http://httpbin.org/get?a=b"
        }
      }
    },
    "status": "OK"
  }
}

Permission to create a Task is governed by the TASK_WRITE role.

HTTP Request

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

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 task disabled?
displayName string optional. human-friendly display name
source object optional. JS ingest procedure
js string optional. javascript to execute.
periodicity number task execution period in milliseconds.

Listing company Tasks

curl "https://octave-api.sierrawireless.io/v5.0/my_company/task" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": [
    {
      "id": "t5eff44ec14c322021cd812be",
      "companyId": "c000000000000000000000006",
      "creationDate": 1593787628423,
      "creatorId": "i000000000000000000000064",
      "destination": "/my_company/task_output/everyday",
      "displayName": "Generate one event every hour",
      "js": "function(events, raw) {var ev = {elems: {}}; if (events && events[0]) { ev.elems.hour = (events[0].elems.hour + 1) % 24; } else { ev.elems.hour = 1; } return {'/my_company/task_output/everyday': [ev] }}",
      "lastEditDate": 1593787628423,
      "lastEditorId": "i000000000000000000000064",
      "lastRun": 1593787688513,
      "nextRun": 1593787708513,
      "periodicity": 3600000,
      "runCt": 4,
      "source": {
        "eventFind": {
          "streamPath": "/my_company/task_output/everyday",
          "options": {
            "limit": 1
          }
        }
      },
      "status": "OK"
    },
    ...
  ]
}

Permission to read Tasks is governed by the TASK_READ role.

HTTP Request

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

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

Reading a Task

curl "https://octave-api.sierrawireless.io/v5.0/my_company/task/t5eff44ec14c322021cd812be" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "t5eff44ec14c322021cd812be",
    "companyId": "c000000000000000000000006",
    "creationDate": 1593787628423,
    "creatorId": "i000000000000000000000064",
    "destination": "/my_company/task_output/everyday",
    "displayName": "Generate one event every hour",
    "js": "function(events, raw) {var ev = {elems: {}}; if (events && events[0]) { ev.elems.hour = (events[0].elems.hour + 1) % 24; } else { ev.elems.hour = 1; } return {'/my_company/task_output/everyday': [ev] }}",
    "lastEditDate": 1593787628423,
    "lastEditorId": "i000000000000000000000064",
    "lastRun": 1593787768508,
    "nextRun": 1593787788508,
    "periodicity": 3600000,
    "runCt": 8,
    "source": {
      "eventFind": {
        "streamPath": "/my_company/task_output/everyday",
        "options": {
          "limit": 1
        }
      }
    },
    "status": "OK"
  }
}

Permission to read Tasks is governed by the TASK_READ role.

HTTP Request

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/task/<task_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 Task

curl -X "PUT" "https://octave-api.sierrawireless.io/v5.0/my_company/task/t5eff44ec14c322021cd812be" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
  "description": "My Task"
}'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "t5eff44ec14c322021cd812be",
    "companyId": "c000000000000000000000006",
    "creationDate": 1593787628423,
    "creatorId": "i000000000000000000000064",
    "destination": "/my_company/task_output/everyday",
    "description": "My Task",
    "displayName": "Generate one event every hour",
    "js": "function(events, raw) {var ev = {elems: {}}; if (events && events[0]) { ev.elems.hour = (events[0].elems.hour + 1) % 24; } else { ev.elems.hour = 1; } return {'/my_company/task_output/everyday': [ev] }}",
    "lastEditDate": 1593787628423,
    "lastEditorId": "i000000000000000000000064",
    "lastRun": 1593787768508,
    "nextRun": 1593787788508,
    "periodicity": 3600000,
    "runCt": 8,
    "source": {
      "eventFind": {
        "streamPath": "/my_company/task_output/everyday",
        "options": {
          "limit": 1
        }
      }
    },
    "status": "OK"
  }
}

Permission to update a Task is governed by the TASK_WRITE role.

HTTP Request

PUT https://octave-api.sierrawireless.io/v5.0/<company_name>/task/<task_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 task disabled?
displayName string optional. human-friendly display name
source object optional. JS ingest procedure
js string optional. javascript to execute.
periodicity number task execution period in milliseconds.

Deleting a Task

curl -X "DELETE" "https://octave-api.sierrawireless.io/v5.0/my_company/task/t5eff44ec14c322021cd812be" \
     -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 Task is governed by the TASK_DELETE role.

HTTP Request

DELETE https://octave-api.sierrawireless.io/v5.0/<company_name>/task/<task_id>

Request Headers

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

Simulating a Task

curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/my_company/task/simulate" \
    -H 'X-Auth-Token: <token>' \
    -H 'X-Auth-User: <user>' \
    -H 'Content-Type: application/json' \
     -d $'{
    "js": "function (events, response){console.log(events);console.log(response)}",
  "source": {
    "http": {
            "parser": {
                "jsonpath": {
                    "nodes": {
                        "elems": {
                            "headers": "headers.Accept",
                            "a": "args.a"
                        }
      },
                    "root": "$"
    }
  },
            "get": {
                "url": "http://httpbin.org/get?a=b"
            }
        }
    }
}'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "events": null,
    "task": {
      "js": "function (events, response){console.log(events);console.log(response)}",
      "periodicity": 20000,
      "source": {
        "http": {
          "parser": {
            "jsonpath": {
              "nodes": {
                "elems": {
                  "headers": "headers.Accept",
                  "a": "args.a"
                }
              },
              "root": "$"
            }
          },
          "get": {
            "url": "http://httpbin.org/get?a=b"
          }
        }
      }
    },
    "log": [
      [
        {
          "path": null,
          "metadata": null,
          "streamId": null,
          "elems": {
            "headers": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2",
            "a": "b"
          },
          "lastEditDate": null,
          "generatedDate": null,
          "location": null,
          "id": null,
          "creationDate": null,
          "hash": null,
          "tags": {}
        }
      ],
      {
        "response": {
          "headers": {
            "Server": [
              "gunicorn/19.9.0"
            ],
            "Access-Control-Allow-Origin": [
              "*"
            ],
            "Access-Control-Allow-Credentials": [
              "true"
            ],
            "Connection": [
              "keep-alive"
            ],
            "Content-Length": [
              "321"
            ],
            "Date": [
              "Fri, 03 Jul 2020 14:57:58 GMT"
            ],
            "Content-Type": [
              "application/json"
            ]
          },
          "message": "OK",
          "contentType": "application/json",
          "content": "{\n  \"args\": {\n    \"a\": \"b\"\n  }, \n  \"headers\": {\n    \"Accept\": \"text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2\", \n    \"Host\": \"httpbin.org\", \n    \"User-Agent\": \"Brooklyn\", \n    \"X-Amzn-Trace-Id\": \"Root=1-5eff4776-6bf977d5f0a0651141aa6653\"\n  }, \n  \"origin\": \"176.129.161.48\", \n  \"url\": \"http://httpbin.org/get?a=b\"\n}\n",
          "status": 200
        },
        "line_no": 1,
        "events": [
          {
          "path": null,
            "metadata": null,
            "streamId": null,
          "elems": {
              "headers": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2",
              "a": "b"
            },
            "lastEditDate": null,
            "generatedDate": null,
            "location": null,
            "id": null,
            "creationDate": null,
            "hash": null,
            "tags": {}
        }
      ]
      },
      {
        "headers": {
          "Server": [
            "gunicorn/19.9.0"
          ],
          "Access-Control-Allow-Origin": [
            "*"
          ],
          "Access-Control-Allow-Credentials": [
            "true"
          ],
          "Connection": [
            "keep-alive"
          ],
          "Content-Length": [
            "321"
          ],
          "Date": [
            "Fri, 03 Jul 2020 14:57:58 GMT"
          ],
          "Content-Type": [
            "application/json"
          ]
        },
        "message": "OK",
        "contentType": "application/json",
        "content": "{\n  \"args\": {\n    \"a\": \"b\"\n  }, \n  \"headers\": {\n    \"Accept\": \"text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2\", \n    \"Host\": \"httpbin.org\", \n    \"User-Agent\": \"Brooklyn\", \n    \"X-Amzn-Trace-Id\": \"Root=1-5eff4776-6bf977d5f0a0651141aa6653\"\n  }, \n  \"origin\": \"176.129.161.48\", \n  \"url\": \"http://httpbin.org/get?a=b\"\n}\n",
        "status": 200
    }
    ],
    "errors": []
  }
}

This endpoint can be called on either an existing or non-existing Task. The endpoint performs the action specified in the Source field and then evaluates the parsed output against the Task JavaScript (if any); however, events are not persisted. (Note that this behavior is different than the behavior of Cloud Action Simulate.)

HTTP Request

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

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

Request Headers

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

Request JSON Object

Task Simulate using a non-existing Task takes as input a Task object, using the parameters outlined above.

Source Object

## Create task with source object eventFind
curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/my_company/task" \
    -H 'X-Auth-Token: <token>' \
    -H 'X-Auth-User: <user>' \
    -H 'Content-Type: application/json; charset=utf-8' \
    -d $'{
    "destination": "my_company/task_output/sampleTask",
    "periodicity": "20000",
    "js": "function(ds, raw){return {\"my_company/task_output/findEvend\":ds};}",
    "source": {
        "eventFind": {
            "streamId": "s5e5d6db4772f39d6b996da59",
            "options": { 
                "limit": 1
            }
        }
    },
    "displayName": "A sample task with eventFind source"
}'
## Response for task with eventFind source
{
    "head": {
        "status": 201,
        "ok": true,
        "messages": [
            "Your request has been processed successfully. A new resource has been created."
        ],
        "errors": [],
        "references": {}
    },
    "body": {
        "id": "t5e5f2ae6772f39d6b9977d2e",
        "companyId": "c5bae617e90f45d7d11710087",
        "creationDate": 1583295206808,
        "creatorId": "i5dea9ae317900a4b57f95b39",
        "destination": "my_company/task_output/sampleTask",
        "displayName": "A sample task with eventFind source",
        "js": "function(ds, raw){return {\"my_company/task_output/findEvend\":ds};}",
        "lastRun": 0,
        "nextRun": 0,
        "periodicity": 20000,
        "runCt": 0,
        "source": {
            "eventFind": {
                "streamId": "s5e5d6db4772f39d6b996da59",
                "options": {
                    "limit": 1
                }
            }
        },
        "status": "OK"
    }
}
## Create task with source object http
curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/my_company/task" \
    -H 'X-Auth-Token: <token>' \
    -H 'X-Auth-User: <user>' \
    -H 'Content-Type: application/json; charset=utf-8' \
    -d $'{
    "destination": "my_company/task_output/sampleHttpTask",
    "periodicity": "20000",
    "js": 
    "function f(events,response){ return {\"my_company/task_output/sampleHttpTask": [{\"elems\":{\"a\":response.status, \"b\":a}}]};}",
    "source": {
        "http": {
        "get": {
            "url": "http://httpbin.org/get?a=b"
            },
        "parser": {"jsonpath": {"root": "$", "nodes":{"headers":"headers.Accept", "a":"args.a"} } }
        }
    },
    "displayName": "A sample task with http source"
}'
## Response for task with http source
{
    "head": {
        "status": 201,
        "ok": true,
        "messages": [
            "Your request has been processed successfully. A new resource has been created."
        ],
        "errors": [],
        "references": {}
    },
    "body": {
        "id": "t5e5fdd9dd6f7e7f7f9d3c955",
        "companyId": "c5bae617e90f45d7d11710087",
        "creationDate": 1583340957885,
        "creatorId": "i5dea9ae317900a4b57f95b39",
        "destination": "my_company/task_output/sampleHttpTask",
        "displayName": "A test task-3.2",
        "js": "function f(events,response){ return {\"my_company/task_output/sampleHttpTask\": [{\"elems\":{\"a\":response.status, \"b\":a}}]};}",
        "lastRun": 0,
        "nextRun": 0,
        "periodicity": 20000,
        "runCt": 0,
        "source": {
            "http": {
                "parser": {
                    "jsonpath": {
                        "nodes": {
                            "elems": {
                                "headers": "headers.Accept",
                                "a": "args.a"
                            }
                        },
                        "root": "$"
                    }
                },
                "get": {
                    "url": "http://httpbin.org/get?a=b"
                }
            }
        },
        "status": "OK"
    }
}

The Source object defines declaratively how data will be ingested into the Task's JavaScript function. Retrieved elements will be converted into the form of events if necessary (e.g, for http sources). If Source is unspecified, the Task JavaScript will be executed with empty input arguments.

Name Description
eventFind or http required. configuration object

HTTP Configuration Object

Name Description
parser required. possible values are jsonpath, xpath, rss
get, put, post, or delete required. value is an HTTP Method object

HTTP Method Object

name description
url required.
headers optional.
data optional. request body.

EventFind Configuration Object

name description
streamId or streamPath required.
options optional. EventFind options object.

EventFind Options Object

Name Description
limit optional.
start optional.
sort optional.
order optional.
filter optional.

Blueprint Object

Creating a Blueprint

curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/my_company/blueprint" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
      "displayName": "My Blueprint",
      "localActions": {
        "l000000000000000000000000": {
          "version": 1
        }
      },
      "observations": {
        "/redSensor/position/value": {
          "position": {
            "period": 3600,
            "destination": "store",
            "description": "Send Position Periodically"
          }
        }
      },
      "state": {
        "/redSensor/position/period": 60
      }
     }"
}'
{
  "head": {
    "status": 201,
    "ok": true,
    "messages": [
      "Your request has been processed successfully. A new resource has been created."
    ],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "b5b86d4d88c633a1add9f181d",
    "companyId": "c5adf465baa02b35e0cce78c2",
    "creationDate": 1535562968521,
    "creatorId": "i5adf463709159f4dde43a86f",
    "displayName": "My Blueprint",
    "localActions": {
      "l000000000000000000000000": {
        "version": 1
      }
    },
    "observations": {
      "/redSensor/position/value": {
        "position": {
          "period": 3600,
          "destination": "store",
          "description": "Send Position Periodically"
        }
      }
    },
    "state": {
      "/redSensor/position/period": 60
    },
    "version": 1
  }
}

HTTP Request

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

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
displayName string A friendly name for the Device
localActions object Defines the Local Actions (represented by IDs and Versions) to be deployed to the Device
observations object A collection of "routes" from Resources to other Resources, Local Actions, or the Cloud
state object The default values assigned to Resources on the Device

Listing company Blueprints

curl "https://octave-api.sierrawireless.io/v5.0/my_company/blueprint" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": [
    {
      "id": "b5b86d4d88c633a1add9f181d",
      "companyId": "c5adf465baa02b35e0cce78c2",
      "creationDate": 1535562968521,
      "creatorId": "i5adf463709159f4dde43a86f",
      "displayName": "My Blueprint",
      "localActions": {
        "l000000000000000000000000": {
          "version": 1
        }
      },
      "observations": {
        "/redSensor/position/value": {
          "position": {
            "period": 3600,
            "destination": "store",
            "description": "Send Position Periodically"
          }
        }
      },
      "state": {
        "/redSensor/position/period": 60
      },
      "version": 1
    },
    ...
  ]
}

HTTP Request

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

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

Reading a Blueprint

curl "https://octave-api.sierrawireless.io/v5.0/my_company/blueprint/b5b86d4d88c633a1add9f181d" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "b5b86d4d88c633a1add9f181d",
    "companyId": "c5adf465baa02b35e0cce78c2",
    "creationDate": 1535562968521,
    "creatorId": "i5adf463709159f4dde43a86f",
    "displayName": "My Blueprint",
    "localActions": {
      "l000000000000000000000000": {
        "version": 1
      }
    },
    "observations": {
      "/redSensor/position/value": {
        "position": {
          "period": 3600,
          "destination": "store",
          "description": "Send Position Periodically"
        }
      }
    },
    "state": {
      "/redSensor/position/period": 60
    },
    "version": 1
  }
}

HTTP Request

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/blueprint/<blueprint_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 Blueprint

curl -X "PUT" "https://octave-api.sierrawireless.io/v5.0/my_company/blueprint/b5b86d4d88c633a1add9f181d" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>' \
     -d $'{
    "state": {
      "/redSensor/position/period": 60
      "/cloudInterface/developer_mode/enable": false,
    }
    }"
}'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": {
    "id": "b5b86d4d88c633a1add9f181d",
    "companyId": "c5adf465baa02b35e0cce78c2",
    "creationDate": 1535562968521,
    "creatorId": "i5adf463709159f4dde43a86f",
    "displayName": "My Blueprint",
    "localActions": {
      "l000000000000000000000000": {
        "version": 1
      }
    },
    "observations": {
      "/redSensor/position/value": {
        "position": {
          "period": 3600,
          "destination": "store",
          "description": "Send Position Periodically"
        }
      }
    },
    "state": {
      "/redSensor/position/period": 60
      "/cloudInterface/developer_mode/enable": false,
    }
    "version": 2
  }
}

HTTP Request

PUT https://octave-api.sierrawireless.io/v5.0/<company_name>/blueprint/<blueprint_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
state object The default values assigned to Resources on the Device

Deleting a Blueprint

curl -X "DELETE" "https://octave-api.sierrawireless.io/v5.0/my_company/blueprint/b5b86d4d88c633a1add9f181d" \
     -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>/blueprint/<blueprint_id>

Request Headers

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

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

Listing company Groups

curl "https://octave-api.sierrawireless.io/v5.0/my_company/group" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "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

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

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>'
{
  "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 Read Cloud Actionx
CLOUD_ACTION_WRITE Create and Edit Cloud Actions
CLOUD_ACTION_DELETE Delete Cloud Actions
CLOUD_CONNECTOR_READ Read Cloud Connectors
CLOUD_CONNECTOR_WRITE Create and Edit Cloud Connectors
CLOUD_CONNECTOR_DELETE Delete Cloud Connectors
LOCAL_ACTION_READ Read Local Actions
LOCAL_ACTION_WRITE Create and Edit Local Actions
LOCAL_ACTION_DELETE Delete Local Actions
BLUEPRINT_READ Read Blueprints
BLUEPRINT_WRITE Create and Edit Blueprints
BLUEPRINT_DELETE Delete Blueprints
TASK_READ Read Tasks
TASK_WRITE Create and Edit Tasks
TASK_DELETE Delete Tasks
COMPANY_WRITE Edit Company Object
COMPANY_MEMBERSHIP_EDIT Allows Identity to control Copmany Membership

Listing company Shares

curl "https://octave-api.sierrawireless.io/v5.0/my_company/share" \
     -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

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

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

Listing company Tokens

curl "https://octave-api.sierrawireless.io/v5.0/my_company/token" \
     -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

Request Headers

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

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

Firmware versions

Listing system-wide Firmware versions

curl "https://octave-api.sierrawireless.io/v5.0/firmware"
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": [
    {
      "creationDate": 1586187115697,
      "creatorId": "i000000000000000000000001",
      "id": "y5e8b4b6b043f2a1b4bdcd4b9",
      "version": "1.2.3"
    },
    {
      "creationDate": 1586188358741,
      "creatorId": "i000000000000000000000001",
      "id": "y5e8b5046043f2a2d6d4e4fbb",
      "version": "1.2.4"
    }
  ]
}

Listing system-wide (i.e, available for all companies) Firmware versions doesn't require authentication, so this call doesn't need auth headers.

HTTP Request

GET https://octave-api.sierrawireless.io/v5.0/firmware

Listing company Firmware versions

curl "https://octave-api.sierrawireless.io/v5.0/my_company/firmware" \
    -H 'X-Auth-Token: <token>' \
    -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": [
    {
      "creationDate": 1586187115697,
      "creatorId": "i000000000000000000000001",
      "companyId": "c000000000000000000000006",
      "id": "y5e8b4b6b043f2a1b4bdcd4b9",
      "version": "1.2.2"
    },
    {
      "creationDate": 1586187115697,
      "creatorId": "i000000000000000000000001",
      "id": "y5e8b4b6b043f2a1b4bdcd4b9",
      "version": "1.2.3"
    },
    {
      "creationDate": 1586188358741,
      "creatorId": "i000000000000000000000001",
      "id": "y5e8b5046043f2a2d6d4e4fbb",
      "version": "1.2.4"
    }
  ]
}

This call returns company-specific Firmware versions in addition to system-wide ones.

HTTP Request

GET https://octave-api.sierrawireless.io/v5.0/my_company/firmware

Request Headers

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

Release Notes

Listing system-wide Release Notes

curl "https://octave-api.sierrawireless.io/v5.0/release-note"
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": [
    {
      "creationDate": 1586187115697,
      "creatorId": "i000000000000000000000001",
      "id": "y5e8b4b6b043f2a1b4bdcd4b9",
      "version": "5.3.1",
      "notes": "# Octave API version 5.3.1....",
    },
    {
      "creationDate": 1586188358741,
      "creatorId": "i000000000000000000000001",
      "id": "y5e8b5046043f2a2d6d4e4fbb",
      "version": "5.4.0",
      "notes": "# Octave API version 5.4.0....",
    }
  ]
}

Listing system-wide (i.e, available for all companies) Release Notes doesn't require authentication, so this call doesn't need auth headers.

HTTP Request

GET https://octave-api.sierrawireless.io/v5.0/release-note

Listing company Release Note versions

curl "https://octave-api.sierrawireless.io/v5.0/my_company/release-note" \
     -H 'X-Auth-Token: <token>' \
     -H 'X-Auth-User: <user>'
{
  "head": {
    "status": 200,
    "ok": true,
    "messages": [],
    "errors": [],
    "references": {}
  },
  "body": [
    {
      "creationDate": 1586187115697,
      "creatorId": "i000000000000000000000001",
      "companyId": "c000000000000000000000006",
      "id": "y5e8b4b6b043f2a1b4bdcd4b9",
        "version": "5.3.0",
        "notes": "# Octave API version 5.3.0...."
    },
    {
      "creationDate": 1586187115697,
      "creatorId": "i000000000000000000000001",
      "id": "y5e8b4b6b043f2a1b4bdcd4b9",
      "version": "5.3.1",
      "notes": "# Octave API version 5.3.1...."
    },
    {
      "creationDate": 1586188358741,
      "creatorId": "i000000000000000000000001",
      "id": "y5e8b5046043f2a2d6d4e4fbb",
      "version": "5.4.0",
      "notes": "# Octave API version 5.4.0...."
    }
  ]
}

This call returns company-specific Release Notes in addition to system-wide ones.

HTTP Request

GET https://octave-api.sierrawireless.io/v5.0/my_company/release-note

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

*Caveat: Element Value Comparisons cannot be used in filters when Analyzing Events

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 whose 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 [[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 thedescription or displayName field in Events.

Find all Events in the given Stream where the value of "description" field ( part of "elems") matches the regex /[cC]lojure/:

GET https://octave-api.sierrawireless.io/v5.0/<company_name>/event/<stream_id>?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 examples 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

Specifies 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.

Heatbeat

{
  "type": "heartbeat"
}

The client should periodically send a heartbeat message to keep the session alive. Sessions have a timeout of 6 minutes. Sending a heartbeat will touch the timeout and keep it from expiring.

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, cloud-connector, external, group, local-action, 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.

Dash Visualization Example

Visualization Overview

In this example we'll set up some MangOH Reds to stream gyro and accelerometer data to the cloud, and then use the Dash framework to build a custom visualization.

Setup MangOH Observations

First we'll set up two observations on all of the MangOHs to be used in the visualization. Ensure that the /redSensor/accel and /redSensor/gyro resources are configured to poll regularly (5 seconds should be fine). Then create an observation to send data to the cloud immediately.

Setup MangOH Tags

Next we'll set up a tag on the MangOHs to include in the demo. I'll use dashdemo: true.

Get a Mapbox access Token

We'll be using Mapbox for our map. Head over there and get an access token.

Setup python and modules

certifi==2018.10.15
chardet==3.0.4
Click==7.0
dash==0.28.2
dash-core-components==0.39.0rc4
dash-html-components==0.13.2
dash-renderer==0.14.3
decorator==4.3.0
Flask==1.0.2
Flask-Caching==1.4.0
Flask-Compress==1.4.0
idna==2.7
ipython-genutils==0.2.0
itsdangerous==0.24
Jinja2==2.10
jsonschema==2.6.0
jupyter-core==4.4.0
MarkupSafe==1.0
nbformat==4.4.0
numpy==1.16.1
pandas==0.24.1
plotly==3.3.0
python-dateutil==2.8.0
pytz==2018.5
requests==2.19.1
retrying==1.3.3
six==1.11.0
traitlets==4.3.2
urllib3==1.23
Werkzeug==0.14.1

A complete list of modules can be found to the right. You can install them directly or use virtualenv to keep your system python modules untouched. You'll need to use Python 3.

Create Token for Demo

curl -X "POST" "https://octave-api.sierrawireless.io/v5.0/<my_company>/token" \
     -H 'X-Auth-User: <my_username>' \
     -H 'X-Auth-Token: <my_master_token>' \
     -H 'Content-Type: application/json; charset=utf-8' \
     -d $'{
  "paths": {
    "/<my_company>/devices": {
      "eventWrite": false,
      "write": false,
      "read": true,
      "eventRead": true
    }
  },
  "description": "Token to read devices and device streams for Demo"
}'

Since our application does not require full read/write access to Octave, we are going to create a token with limited access. The POST command to the right will create a token that can only read devices and their streams.

The code

# -*- coding: utf-8 -*-
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly
import plotly.graph_objs as go
import requests
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
from dash.dependencies import Input, Output, State
from datetime import datetime
from flask_caching import Cache
from os import getenv
from time import sleep

default_location = {'lat': 49.172477, 'lon': -123.071298}
creds = { 'X-Auth-Token': getenv('TOKEN', '<snip>') }
mapbox_access_token = getenv('MAPBOX_ACCESS', '<snip>')
company = getenv('COMPANY', '<snip>')
device_update_interval = int(getenv('DEVICE_UPDATE_INTERVAL', '20'))

external_stylesheets = ['https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

cache = Cache(app.server, config={
    'CACHE_TYPE': 'filesystem',
    'CACHE_DIR': 'cache-directory'
})

def update_devices():
    global devices
    print('Updating Devices')
    url = 'https://octave-api.sierrawireless.io/v5.0/{}/device/?filter=tags.dashdemo%3D%3D%22true%22'.format(company)
    all_devs = [d for d in requests.get(url, headers=creds).json()['body']]
    for d in all_devs:
        if 'location' not in d.keys():
            d.update(location=default_location)
    devices = all_devs

def update_devices_every(period=device_update_interval):
    while True:
        update_devices()
        sleep(period)

update_devices()
executor = ThreadPoolExecutor(max_workers=1)
executor.submit(update_devices_every)
sleep(2) # make sure devices get loaded

@cache.memoize(timeout=2)
def get_events_for_device_stream(device_name, stream_name):
    url = 'https://octave-api.sierrawireless.io/v5.0/%s/event/?path=/%s/devices/%s/%s' % (company,company, device_name, stream_name)
    return sorted([e for e in requests.get(url, headers=creds).json()['body']], key=lambda x: x['generatedDate'])

def get_gyro_for_device(device_name):
    events = get_events_for_device_stream(device_name, 'gyro2c')

    def scatter_for_dimension(dim):
        return go.Scatter(
            x=[datetime.fromtimestamp(e['generatedDate']/1000.0) for e in events],
            y=[e['elems']['redSensor']['gyro'][dim] for e in events],
            name='Gyro %s' % dim
        )

    return [scatter_for_dimension(i) for i in ['x', 'y', 'z']]

def get_accel_for_device(device_name):
    events = get_events_for_device_stream(device_name, 'accel2c')

    def scatter_for_dimension(dim):
        return go.Scatter(
            x=[datetime.fromtimestamp(e['generatedDate']/1000.0) for e in events],
            y=[e['elems']['redSensor']['accel'][dim] for e in events],
            name='Accel %s' % dim
        )

    return [scatter_for_dimension(i) for i in ['x', 'y', 'z']]


def get_map_data_from_devices():
    return [ dict(
        type = 'scattermapbox',
        lon = [d['location']['lon'] for d in devices],
        lat = [d['location']['lat'] for d in devices],
        text = [d['name'] for d in devices],
        mode = 'markers',
        marker = dict(
            size = 8,
        ))]

def generate_layout():
    return html.Div(children=[
        html.Div(className='row', children=[
            html.Div(className='col-12', children=[
                dcc.Graph(id='live-update-map'),
                dcc.Interval(
                    id='interval-component',
                    interval=10*1000,
                    n_intervals=0
                )
            ]),
        ]),
        html.Div(className='row', children=[
            html.Div(className='col-6', children=[
                dcc.Graph(id='gyro-time-series')
            ]),
            html.Div(className='col-6', children=[
                dcc.Graph(id='accel-time-series')
            ]),
        ])
    ])

app.layout = generate_layout()

@app.callback(
    Output('live-update-map', 'figure'),
    [Input('interval-component', 'n_intervals')],
    [State('live-update-map', 'relayoutData')]
)
def update_location_map(n, mapdata):
    fig = {
        'data': get_map_data_from_devices(),
        'layout': {
            'autosize': True,
            'title': '{} MangOHs tagged with dashdemo: true'.format(company),
            'mapbox': {
                'center': {},
                'accesstoken': mapbox_access_token,
                'center': {
                    'lat': 39.8283,
                    'lon': -98.5795
                },
                'zoom': 2,
            },
            'uirevision': 1
        },
    }

    if not mapdata or 'mapbox.center' not in mapdata.keys(): mapdata = {}
    fig['layout']['mapbox']['center'] = mapdata.get('mapbox.center', { 'lat': 39.8283, 'lon': -98.5795 })
    fig['layout']['mapbox']['zoom'] = mapdata.get('mapbox.zoom', 2)
    fig['layout']['mapbox']['bearing'] = mapdata.get('mapbox.bearing', 0)
    fig['layout']['mapbox']['pitch'] = mapdata.get('mapbox.pitch', 0)
    return fig

@app.callback(
    Output('gyro-time-series', 'figure'),
    [Input('live-update-map', 'hoverData')]
)
def update_gyro(hoverData):
    if not hoverData: return {}
    device_name = hoverData['points'][0]['text']
    gyro_data = get_gyro_for_device(device_name)
    return {
        'data': gyro_data,
        'layout': {
            'title': 'Gyro Data for "%s"' % device_name
        }
    }

@app.callback(
    Output('accel-time-series', 'figure'),
    [Input('live-update-map', 'hoverData')]
)
def update_accel(hoverData):
    if not hoverData: return {}
    device_name = hoverData['points'][0]['text']
    accel_data = get_accel_for_device(device_name)
    return {
        'data': accel_data,
        'layout': {
            'title': 'Accel Data for "%s"' % device_name
        }
    }

if __name__ == '__main__':
app.run_server(host='127.0.0.1')

Environmental Variables

update_devices functions

We use a background thread here to refresh the devices and their locations and store them in a global variable. This avoids having every page load trigger a new GET for the device list.

get_accel|gyro_for_device functions

These functions gather data from the Octave Event API and transform them into a format suitable for the line plots.

get_map_data_from_devices function

This function transforms the global device data into a format suitable for rendering in the map plot.

generate_layout function

This is the main layout skeleton. The data is returned by the update functions described below.

update functions

Each of these functions are tied to events that occur on the page; either a periodic timer for the map, or a hover event on a device point. They call the get_ functions above to gather data over REST or use the memoized function return if under the timeout number of seconds.

Running the demo

Save and execute the python code and browse to http://127.0.0.1:8050/.