0% found this document useful (0 votes)
4 views33 pages

DeviceManager API Guide

The AssetLink DeviceManager Web API provides a cloud-based interface for customers to interact with their remotely monitored assets, allowing for data normalization and command execution across various device types. The API organizes incoming data into a structured format of key:value pairs, Points, Moments, Devices, and Groups, facilitating efficient data management and retrieval. It supports a variety of commands and filters to enable users to query and manipulate device data effectively.

Uploaded by

dantocom
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views33 pages

DeviceManager API Guide

The AssetLink DeviceManager Web API provides a cloud-based interface for customers to interact with their remotely monitored assets, allowing for data normalization and command execution across various device types. The API organizes incoming data into a structured format of key:value pairs, Points, Moments, Devices, and Groups, facilitating efficient data management and retrieval. It supports a variety of commands and filters to enable users to query and manipulate device data effectively.

Uploaded by

dantocom
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

AssetLink

DeviceManager
Web API
Revision 4, September 2020

AssetLink Global LLC

[Link]

service@[Link]

+1 (303) 862.8745

Fax: +1 (720) 862.3515

AssetLink Proprietary
Revision History

Revision Date of Release Purpose


Initial Draft 9/16/2016 Based on Portal Fundamental API, as implemented
1 3/3/2017 Updated following lessons-learned from original
implementation. Changed name from Data Portal to
DeviceManager.
2 5/16/2018 Removed deprecated Device API commands
3 10/1/2018 Added sensor remote commands
4 9/1/2020 Added Device getFirmwareVersion API command

AssetLink Proprietary
Introduction
The AssetLink DeviceManager is the cloud-based server that forms the logical interface between
AssetLink customers and their remotely monitored assets. The over-the-air (OTA) formats for
interacting with devices like the AssetPack 3 are necessarily compact and rely on a great deal of
context and shared format information. Over the Web, conversely, data and their context can
both be shared — for example, a geographic position might be sent over the air as the
compressed but obtuse 48-bit value 0x375b51c9375a, but a Web API has the luxury of offering
this as {"Lat":38.92268,"Lon":-77.03969}, increasing comprehensibility and reducing the
possibility of gross errors in parsing.

This API is how customers can have their own server systems query the DeviceManager,
receiving information and sending commands.

It also functions as a mechanism for data normalization, where different remote devices, from
different sources in different applications with different over-the-air protocols, can have their
common information shared in a common way. One unit in the field may be a simple tracking
device using Iridium, another may be a sophisticated medicine-temperature-sensing device
using cellular, but they are both reporting position, and that position information comes
through in a common way from both of them via this API.

This API recognizes that in general, requests into the Manager wish to select some set of
objects, and then do something with them. Select a bunch of remote devices, and send a
schedule to all of them. Select a series of Moments, and get the Points for all of them. Select all
the children of a Grp, and put an Alert on any of them that have gone quiet. Some of these
seem to lend themselves to REST terminology, but it ends up being a little tangential to the idea
of GET / PUT / POST / DELETE; with actual pieces of hardware involved, it’s more clearly select
and then do.

Groups, Devices, Moments, and Points


The AssetLink DeviceManager takes in the data provided by any backhaul provider, in whatever
format, using whatever protocol, responding with whatever handshake, that each provider
defines. It takes these incoming data and reduces them to a common structure:

➢ a set of key:value pairs (such as Lat:34.56, Lon:-77.02, Door:open),


➢ which are gathered into associated structures we call Points (a PointLoc[ation] with Lat
and Lon, a PointAlarm saying Hey the door is open),
➢ which are then collected into a structure called a Moment, whose job is to say “At this
time, we learned this information”,
➢ that Moment is added to the Device that just reported this information,
➢ the Device belonging to one or more Groups. Groups can describe what part of the
corporate organization the unit belongs to; or what type of asset the unit is attached to;

AssetLink Proprietary Page 3


or a behavior that the unit should display. Because “Group” is a keyword in databases
and other pieces of infrastructure, the specific method of grouping units in the
DeviceManager is shortened to “Grp”.

This structure – key:values, inside Points, inside Moments, inside Devices, gathered into Grps –
is the consistent structure used for anything that occurs at some time to a device out in the
world. Hardware devices that currently exist, whose report data we already know. Hardware
devices we can envision. Hardware devices we can't yet envision. If this hardware reports
something fairly common, like a location, we record that location in a consistently-named way
so that everything downstream that cares in any way about a location, can recognize that
common name and use it. If this hardware reports something totally unique, it's still a key and a
value in a Point in a Moment in a Device. And anyone downstream – all the way to a particular
user staring at a screen – who recognizes that key, will know what that value means.

As the number of devices increases, gathering them into useful sets becomes a vital way to
organize workflow. A given device may be within several different Grps, which in turn may be
part of different Grp trees. A set of connected Grps typically share a “Subtype”, to describe the
intent of that collection of Grps. The following tree extract is a conceptual example; the actual
set of Grp Subtypes can be organized and customized for each AssetLink customer.

Grp: MightyCorp
Subtype: Company

Grp: MobileAsset
Subtype: Asset

Grp: NorthAmerica
Subtype: Company

Grp: Backhoe
Grp: StayWithinGeofence Subtype: Asset
Subtype: Action
Grp: Texas
Subtype: Company

Unit 123456

AssetLink Proprietary Page 4


The following pages show diagrammatically how the DeviceManager takes in, organizes, and
resupplies unit information in a common form and format. Note the central text is reproduced
on the second following page for easier reading.

AssetLink Proprietary Page 5


AssetLink Proprietary Page 6
AssetLink Proprietary Page 7
Fundamental API Format
Any API request coming into the DeviceManager will look essentially like this:

[ more than one request can be packaged together


{ each request is a JSON object
"file" : "where this command is defined",
"action" : "command name",
"filter" : "objects this command applies to",
"data" : {
command-specific information
}
"limit" : maximum number of results to send,
"offset" : number of initial results to skip
},
other commands following the same format
]

The array of requests is always executed in order, and any failed request causes the remaining
ones not to be attempted.

The response from the server will be an array corresponding 1:1 with the array of instructions,
where each element in the array contains a "result" parameter ("success" or "error"), the
"objects" that were involved, then further details about the execution of the command. For
successful instructions, there will be a "data" element which in turn contains any additional
information specific to that command. For successful instructions where the server suspects the
result was not what the user intended, a "warnings" element is added, with strings describing
what the concern is (e.g., "1 group with name 'NewGroup' already exists" when adding a group).
For unsuccessful instructions, there will be an "error" element which in turn describes what
went wrong. Any unsuccessful instruction will cause all remaining requests in that array to be
ignored.

Optionally, the requester can include an element named 'tag', which is echoed back in the
response. It can be an object, array, or simple string; whatever it is, gets echoed back. The
requester can then use that tag as a convenience to understand what this response is about.

Here is an example of a request to send a “Report ASAP” command to all of the units directly in
the Grp “SandyTest”:

[ { "file" : "remote",
"action" : "report",
"filter" : "(Grp = 'SandyTest')",
"tag" : "Sandy's units should report",
"data" : {}
} ]

AssetLink Proprietary Page 8


Let’s say there were two devices in that Grp. One has transmitted recently, and one named
“New unit” has never yet transmitted. A file commanding both was created, and the Moments
corresponding to these commands going out also created. (N.B.: These are just examples to
bring out the format; not all responses will have exactly these result objects.) The response
back to the requester looks like this:

[ { "result" : "success",
"objects" : { "Devices" : [
{
"deviceid" : 12345,
"system" : "ISB",
"esn" : "300398765",
"name" : "",
"CLASS" : "AP3",
"lasttx" : 17123711,
"lasttxtime" : "2016-06-28 14:04:44",
"affected" : {
"Moment" : [ 66262 ],
"file" : [ "/fileout/irout/[Link]" ]
}
},
{
"deviceid" : 12346,
"system" : "ISB",
"esn" : "300322322",
"name" : "New unit",
"CLASS" : "AP3b",
"lasttxtime" : "1000-01-01 00:00:00",
"affected" : {
"Moment" : [ 66263 ],
"file" : [ "/fileout/irout/[Link]" ]
}
}
] },
"data" : {
"msgtype" : 11
}
"tag" : "Sandy's units should report"
} ]

… where the “affected” element for each Device shows Device-specific information relevant to
(caused by) this request, and the top-level “data” element shows information relevant to this
request that is not tied to a particular Device — in this case, the message type that the devices
were requested to send back.

If unusual things happen in servicing the request, there are two other elements which may show
up. If the "limit" of response objects is reached, an "ObjectLimited" (viz., "MomentLimited")
element will be added to the "objects" structure. If a warning is generated — like, "Careful, you
just added a group with the same name as one that already exists" — a "warnings" list is added
to the response structure.

AssetLink Proprietary Page 9


This document will first explore the “filter” portion of the request — the most critical part of this
API that singularly encapsulates the select concept explored in the Introduction — and then
enumerate the “actions” most globally available. (Individual applications may have custom API
actions developed just for that application.)

Filters
A given action is going to apply to some target type of object: often a Device, a Moment, or a
Grp. The “filter” element thus is intended to say which Devices, Moments, or Grps the action
should be taken on, but do so in an organized and common way.

To that end, we’re going to define what we’ll call the canonical form of the filter, which is simply
a string of the following form:

(<conditional>)

… but which gets considerably more interesting as we expand the definition:

<conditional> ∈ {
TRUE
FALSE
(<conditional>) AND|OR (<conditional>)
<Parameter> <comparator> <value>
<Parameter> IS [NOT] <special>
<Parameter> IN (<value>[,<value>[,…]])
}

<comparator> ∈ {
= equals
!= does not equal
< less than
<= less than or equals
> greater than
>= greater than or equals
LIKE string pattern match
NOT LIKE string pattern not-match
}

<value> is either a number, and is unquoted; or is a string, and is surrounded by single or


double quotes. These comparators and values are passed to the database (parameterized, of

AssetLink Proprietary Page 10


course, for safety); so the application of a comparator to a value will be interpreted according to
MySQL rules.

<special> ∈ {
NULL
we may add other special values for the DeviceManager to
interpret
}

The <Parameter>s, then, are specific to an object type.

Parameters relevant to Grps are:

Grp the name (string) or id (number) of a Grp


GrpType the type (string) of a Grp
GrpSubtype the subtype (string) of a Grp
GrpParams the params (string) of a Grp
GrpNotes the notes (string) of a Grp
GrpBelow this Grp and all Grps below it (branching from it, recursively)

Parameters relevant to Devices are:

Grps above, specifying the Grps that matching Devices must be a leaf of; plus

Device the esn (string) or id (number) of a Device


esn the esn (string) of a Device
DeviceName the name (string) of a Device
lasttxtime the time (string, ISO8601) that the last message was received from a Device

Parameters relevant to Moments are:

Devices above, specifying the Devices whose Moments we are interested in; plus

Moment the dateOriginated (string, ISO8601) or id (number) of a Moment


dateReceived the dateReceived (string, ISO8601) of a Moment

These lists can be expected to grow to expand the capabilities of the API. If there is a particular
filter parameter that would be of use in your company's application, please let us know.

AssetLink Proprietary Page 11


Non-Canonical Forms
For both historical and convenience reasons, the filter can be expressed in a few different ways.
The key is that they are all ultimately reduced to the canonical form. Following are the non-
canonical ways of expressing filters:

Comparators: the following substitutions are performed for <comparator>s:


• == ⇒ =
• eq ⇒ =
• <> ⇒ !=
• ne ⇒ !=
• lt ⇒ <
• le ⇒ <=
• gt ⇒ >
• ge ⇒ >=

Abbreviations: the following substitutions are performed:


• st= ⇒ Moment>=
• et= ⇒ Moment<=
• () ⇒ TRUE

URI form: a request can be sent in the URI instead of or in addition to the POST contents. It is
considered the first request, and its response will be the first one in the returned array. This
URI-form request should still contain at a minimum a "file=" and "action=" to define the request.
From there, the filter information can be expressed in one of two ways:

• filter=URI_encoded_string
• a series of <Parameter>=<value> pairs; in this case the <comparator> is
always =, and there is an implicit “AND” connecting each pair. Note that unrecognized
<Parameter>s are ignored in this case; in all other methods of presenting a filter, an
unrecognized <Parameter> results in rejection of the request.

Web API
There is a single base URL file to call, which then dispatches as needed. This single URL is
[Link] . When a request is received over this API,
if any value is not populated, it uses the following defaults:

▪ The default value for “file”, if no other one is present, is “moment”.


▪ The default value for “action”, if no other one is present, is “get”, or "getMostRecent" if
the file is "moment".
▪ The default value for “data”, if no other one is present, is null.
▪ The default value for “filter”, if no other one is present, is the constant "TRUE".

AssetLink Proprietary Page 12


▪ The default value for "limit" and "offset" is the Boolean "false", which is interpreted to
mean "none". The exception is when file is "moment": then limit defaults to 1000.

One of the first things [Link] does is a first-cut assessment of whether this user is properly
logged in. [Link] can only be called after the caller has established a session via
Portal/[Link], as follows:

Login

The login structure is a single JSON object with a single element, named USER. The user element
in turn contains two elements, the NAME and PASSWORD of the login to be used. Again, this can
only be sent using HTTPS: do not send this (or any) information plaintext.

{ "USER" : {
"NAME" : "login_username",
"PASSWORD" : "login_password"
}
}

The server responds either with

{ "USER" : {
"NAME" : "login_username",
"STATUS" : "VERIFIED"
}
}

on success, or

{ "USER" : {
"NAME" : "login_username",
"STATUS" : "UNVERIFIED"
}
}

on failure. The cause of failure is intentionally not announced for security.

Logout

The logout structure is the smallest and simplest possible, to make it easiest for a session to be
safely and securely closed. It consists of simply

{
"LOGOUT" : true
}

There is no response from the server beyond the 200 HTTP response.

AssetLink Proprietary Page 13


Returning Data
Each action defines what the relevant response is. That said, there are some general guidelines
and patterns:

• Grp responses give all the information about the resultant Grp, plus the ids of
immediate parents and immediate children of the Grp.
• Device responses include all items in the Device’s database row, plus all immediate
parent Grp ids and names.
• Moment responses are grouped under their owning Devices. The Device information
includes all information from the Device response above, except the JSON columns
(‘current’ and ‘extra’) and Grp memberships. Each Moment then contains all the Point
and PointMap information beneath it.
• An action that is about commanding a device remotely gives traceability information on
the command that went out (e.g., its filename, and perhaps the Moment corresponding
to the command if a Moment is created), and may send much less information on the
device itself (perhaps as little as the id and esn).

A given action may certainly deviate from these guidelines if it makes sense to do so for that
action. That said, following are some examples. These would all be encapsulated inside the
"objects" element of the API response.

For a request that responds with a list of Grps:

{ "Grps" : [
{ "grpid" : 589, "name" : "NorthAmerica",
"type" : "Device", "subtype" : "Company",
"params" : "", "notes" : "All units based in North America",
"parents" : [ { "grpid" : 20, "name" : "MightyCorp" } ],
"children" : [
{ "grpid" : 590, "name" : "Texas" },
{ "grpid" : 591, "name" : "NorthDakota" },
{ "grpid" : 592, "name" : "Canada" }
] },

{ "grpid" : 590, "name" : "Texas",


"type" : "Device", "subtype" : "Company",
"params" : null, "notes" : "All units based in Texas",
"parents" : [ { "grpid" : 589, "name" : "NorthAmerica" } ],
"children" : [] },

{ "grpid" : 591, "name" : "NorthDakota",


"type" : "Device", "subtype" : "Company",
"params" : null, "notes" : "All units based in North Dakota",
"parents" : [ { "grpid" : 589, "name" : "NorthAmerica" } ],
"children" : [] },

AssetLink Proprietary Page 14


{ "grpid" : 592, "name" : "Canada",
"type" : "Device", "subtype" : "Company",
"params" : null, "notes" : "Units transiting Canada",
"parents" : [ { "grpid" : 589, "name" : "NorthAmerica" } ],
"children" : [] }
] }

For a request that responds with a list of Devices:

{ "Devices" : [
{ "deviceid" : 12612, "name" : "",
"system" : "ISB", "esn" : "30023495122", "CLASS" : "AP3b",
"lasttx" : 18531436, "lasttxtime" : "2016-07-26T14:42:06Z",
"current" : { "Lat":41.464022, "Lon":-75.552142 }
"extras" : []
"parents" : [
{ "grpid" : 589, "name" : "NorthAmerica" },
{ "grpid" : 104, "name" : "Generator" }
] }
] }

For a request that responds with a list of Moments:

{ "Moments" : [
{ "deviceid" : 12612, "name" : "",
"system" : "ISB", "esn" : "30023495122", "CLASS" : "AP3b",
"lasttx" : 18531436, "lasttxtime" : "2016-07-26T14:42:06Z",
"moments" : [
{ "momentid" : 18531400,
"dateOriginated" : "2016-07-26T14:00:04Z",
"dateReceived" : "2016-07-26T14:00:41Z",
"points": [
{ "Point": { "ModeChange": "4 – In Motion" } },
{ "PointLoc": {"Lat":41.464030, "Lon":-75.553450}}
] },
{ "momentid" : 18531436,
"dateOriginated" : "2016-07-26T14:42:06Z",
"dateReceived" : "2016-07-26T14:43:01Z",
"points": [
{ "PointLoc": {"Lat":41.464022, "Lon":-75.552142}},
{ "PointAlert": {"Engine":"On", "Level":2}}
] }
] }
] }

For a request that is an outgoing command, the response is specific to that command, but is
typically the Device that was affected:

{ "Devices" : [
{
"deviceid" : 12345,

AssetLink Proprietary Page 15


"system" : "ISB",
"esn" : "300398765",
"name" : "",
"CLASS" : "AP3i13",
"lasttx" : 17123711,
"lasttxtime" : "2016-06-28 14:04:44",
"affected" : {
"momentid" : 66262,
"file" : "fileout/irout/[Link]"
}
},
{
"deviceid" : 12346,
"system" : "SWV",
"esn" : "3003SKY22",
"name" : "New unit",
"CLASS" : "IDP800",
"affected" : {
"momentid" : 66263,
"serversaid" : "200 OK { \"forwardid\":41414 }"
}
}
] }

AssetLink Proprietary Page 16


Globally Available Actions
This API will continue to grow and evolve. New files and actions will be added as time goes on
and needs develop, still adhering to the Fundamental API defined above. Application-specific
actions will be created for particular customers, and if they prove useful to a wider audience,
will be pulled into this list.

Shown here are the actions that are available to all users at publication time. They are grouped
by file name (the "file" element in the API request). Each action has an access level that the
login must meet (be numerically equal to or lower than) in order to execute it. Here are the
current descriptions of access levels:

Access level 10: Company Root


Can perform any action

Access level 20: System Manager


Can do anything (make groups, move devices, etc.) except make new logins

Access level 30: System Monitor


Can acknowledge alarms

Access level 40: Viewer


Can view information but not change it

We are also exploring turning access into a field of flags, so there is not a single linear
progression of capabilities, but a customer-defined set of capabilities a given login is allowed to
possess.

AssetLink Proprietary Page 17


File: grp
All actions in this file are about getting or setting Grp information. All filters are only those
relevant to Grp.

Action: get
Return all of the Grps that match the given filter.
Access level: 105
No data
Return:
"Grps": all Grps that match the given filter

Action: addCompany
Adds a Grp of type:Device, subtype:Company. This kind of Grp is intended to organize units by
their role within an organization.
Access level: 35
Data:
“name”: string, name of the new Grp to add
“params” (optional): JSON string, for future expansion
“notes” (optional): string, explanatory text of what this Grp is
“parent": string or number, or array of strings/numbers, the name or id of the Grp that is
to be this new Grp’s immediate parent; can be an array for multiple parents
Return:
"Child": newly added Grp
"Parent": parent Grp(s) the child was connected to

Action: addContact
Adds a Grp of type:Contact. The filter results are respected as the set of available Grps to use as
“parent” (they need not also be “Contact”).
Access level: 35
Data:
“name”: string, name of the new Grp to add
“subtype” (optional): string, ‘PhoneBook’ for creating a set of contact information for
the entire company
“params” (optional): JSON string, for future expansion
“notes” (optional): string, explanatory text of what this Grp is
“parent": string or number, or array of strings/numbers, the name or id of the Grp that is
to be this new Grp’s immediate parent; can be an array for multiple parents
Return:
"Child": newly added Grp
"Parent": parent Grp(s) the child was connected to

Action: connect

AssetLink Proprietary Page 18


Connect a Grp as a parent to either another Grp (branch) or a target object (leaf). Note that the
filter is still respected as the set of available Grps to use as “parent” and branch-“child”; other
targets are taken from the set of all objects this login can see. Already-existing relationships are
not recreated and are considered not affected.
Access level: 35
Data:
“parent”: string or number, the name or id of the Grp that is to be connected as a new
parent
“childtype”: string, the type of object to connect as child
“child”: string or number, or array of strings/numbers, the id or primary string identifier
(viz. esn for Device) of the object(s) to add as children to the given parent; all must be of
childtype.
Return:
"Child": connected Grp(s)
"Parent": parent Grp(s) all children were connected to

Action: disconnect
Disconnect a Grp from a specified child or children. Note that to move a child from one Grp to
another, a single request array should be sent, the first request being a connect, the second
being a disconnect. This way if the connect has a problem, the disconnect does not occur, and
the child is still accessible through the original Grp.
Access level: 35
Data:
“parent”: string or number, the name or id of the Grp that is to be disconnected as a
parent
“childtype”: string, the type of object to disconnect as child
“child”: string or number, or array of strings/numbers, the id or primary string identifier
(viz. esn for Device) of the object(s) to add as children to the given parent; all must be of
childtype.
Return:
"Child": disconnected Grp(s)
"Parent": parent Grp(s) all children were disconnected from
WARNING:
Disconnections are obeyed as sent! If a disconnect request results in a child Device being
disconnected from its only parent Grp, that Device cannot be accessed by this user any longer.
If a disconnect request results in a child Grp being disconnected from its only parent Grp, that
Grp and all of its unique descendants become inaccessible via this user's login. A mistake of this
type can be remedied by contacting AssetLink — the data are never actually lost — they are
simply disconnected from each other. (If the intent was to disconnect the Grp from this user, of
course, this is not a mistake.)
The most frequent use of the 'disconnect' action is to move a child from one parent Grp to
another. In this case, the new parent Grp must be connected first, followed by the disconnect
from the old parent. As described above, this should be two requests in a single request array,
because if the first request (connect) has a problem, the second request (disconnect) is not
executed.

AssetLink Proprietary Page 19


AssetLink Proprietary Page 20
File: device
All actions in this file are about getting or setting Device information. All filters are those
relevant to Grp (and the Devices at their leaves), plus those relevant to Device.

Action: get
Return all of the Devices that match the given filter.
Access level: 105
No data
Return:
"Devices": all Devices that match the given filter

Action: getFirmwareVersion
Return Device configid and firmware version that match the given filter.
Access level: 105
No data
Return:
"Devices": configid and firmware version for all Devices that match the given filter

Action: setName
Set the ‘name’ field of the Device(s) that match the given filter.
Access level: 35
Data:
“name”: string, new name to set
Return:
"Devices": all affected Devices

Action: provision
Activate Device(s) that match the given filter, on the Device Manager and on the unit’s
backhaul(s), so that data can flow. This may incur a charge.
Access level: 25
Data: (optional)
“plan”: string, the billing plan to use for this Device; default is to use the default billing
plan for the Login making the request
Return:
"Devices": all affected Devices

Action: deprovision
Deactivate Device(s) that match the given filter, on the Device Manager and on the unit’s
backhaul(s), so that charges cease for these Devices. Re-provisioning these same Devices later
may incur a charge.

AssetLink Proprietary Page 21


Access level: 25
No data
Return:
"Devices": all affected Devices

AssetLink Proprietary Page 22


File: moment
All actions in this file are about getting Moments (and their associated Points). All filters are
those relevant to Grp (and the Devices at their leaves), plus those relevant to Device, plus those
relevant to Moments; the return value is a list of Devices that have nonzero matching Moments,
under which are the Moments for that Device, under which are the Points and PointMaps for
that Moment.

Action: get
Return all of the Moments that match the given filter. This will be the most commonly used API
request.
Access level: 105
No data
Return:
"Moments": all Moments that match the given filter, as described above

Action: getMostRecent
Return the Moments that match the given filter, but only one (the most recent) per Device.
Access level: 105
No data
Return:
"Moments": all Device’s most recent Moments that match the given filter, as described
above

AssetLink Proprietary Page 23


File: remote
All actions in this file are about sending a command to a remote device. This is, certainly, a
consequential set of commands, which affect real units in the field, and might incur additional
over-the-air charges depending on the billing plan of the units involved. In general it is healthy
to try out the more intricate commands (like modifyModeSchedule) on a test unit that you have
immediate access to, to make sure you get the desired effect, before deploying such a command
fleetwide.

Not all remote commands will necessarily end up in this file: more involved remote actions
(particularly application-specific ones) will get their own infrastructure and own files. But your
basic commands start here.

Action: report
Send a “report ASAP” command to the remote Device(s) that match the filter.
Access level: 45
Data: (optional)
“msgtype”: number or string, the message type the Device should be prompted to send.
This is of course interpreted according to what kind of Device is being commanded. For
AssetPacks, the default value is 11, “Requested Message”.
Return:
"Devices": all Devices to which the command was sent, with context and traceability for
the command sent

Action: changeMode
Send a “change operating mode” command to the remote Device(s) that match the filter.
Access level: 35
Data:
“mode”: number or string, the new mode as interpreted for the Device being
commanded. For AssetPack3s, this is a number, 2-15.
Return:
"Devices": all Devices to which the command was sent, with context and traceability for
the command sent

Action: modifyRegularReporting
Send a "Report every X hours" command, to be applied to a specified operating mode, to the
remote Device(s) that match the filter. For instance, if a Device has an operating mode
representing "vehicle in motion", and it is currently reporting once per hour, this command can
change that mode to report every half-hour.
Access level: 25
Data:

AssetLink Proprietary Page 24


“mode”: number or string, the mode to be modified as interpreted for the Device being
commanded. For AssetPack3s, this is a number, 2-15.
“rate”: number, how many hours between reports; can be fractional; will be rounded as
needed for specific devices. AssetPack3s, for example, operate on a 15-minute heartbeat, so this
rate would be rounded to the nearest 15 minutes.
Return:
"Devices": all Devices to which the command was sent, with context and traceability for
the command sent

The remaining commands are applicable only to AssetPack units.

Action: modifyModeSchedule
The AssetPack operates on a 15-minute heartbeat. Each operating mode has a schedule
indicating what it should do at every heartbeat. This schedule is expressed as 96 two-bit values,
describing each heartbeat over the course of 24 hours. Each two-bit value can be:
0 Do nothing on this heartbeat
1 Check for incoming messages ('mailbox check') on this heartbeat
2 Perform a custom behavior on this heartbeat
3 Send a position report on this heartbeat; also checks for incoming
messages
What the unit does on every heartbeat — specifically, whether it communicates over the air (1
or 3) or not — is the single largest driver of power consumption on the unit. OTA
communication can happen on every heartbeat in normal-power situations, but must be pared
back when power needs to be conserved. The default behaviors programmed into the unit from
the factory take care of this, and place normal-power modes as the even-numbered modes.
Therefore, typically, modifying the schedule of even-numbered modes is a safe thing to do.
Access level: 25
Data:
“mode”: number, the mode (2-15) to be modified
“schedule”: string, a series of 96 values, each one 0-3, indicating what behavior the unit
should perform at each 15-minute heartbeat over the course of 24 hours
“absolute”: boolean, if true, the first value in "schedule" corresponds to the first
heartbeat after 00:00:00Z (i.e. between 00:00Z and 00:15Z); if false, the first value in "schedule"
corresponds to the first heartbeat after this mode is entered; default false
Return:
"Devices": all Devices to which the command was sent, with context and traceability for
the command sent

Action: modifyGPSMotion
Send a “modify parameters related to GPS-sensor motion detection” command to the remote
Device(s) that match the filter.
When using the GPS to detect motion, the GPS is activated every 5 minutes, and the position (if
found) compared to an earlier position to determine how far the device has traveled.
Default values are noted below. Any data field not present is left unaffected on the unit.
These parameters are only effective in modes that use the GPS sensor to detect motion.

AssetLink Proprietary Page 25


Access level: 25
Data:
“gmotion_start_radius”: number, if the unit moved more than this many meters since its
last position, it is moving; default 250
“gmotion_stop_radius”: number, if the unit moved less than this many meters since a
recent position, it is stopped; default 250
“gmotion_min_gps_sec”: number, the minimum number of seconds between GPS
positions that allows a conclusion of "we've stopped"; default 200
Return:
"Devices": all Devices to which the command was sent, with context and traceability for
the command sent

Action: modifyPiezoMotion
Send a “modify parameters related to piezo-sensor motion detection” command to the remote
Device(s) that match the filter.
The piezo sensor in the AssetPack detects when the unit is shaking. The following parameters
govern how to differentiate between "shaking but not going anywhere" from "shaking because
the unit is traveling down the road".
Default values are noted below. Any data field not present is left unaffected on the unit.
These parameters are only effective in modes that use the piezo sensor to detect motion.
Access level: 25
Data:
“pmotion_start_check_sec”: number, the unit watches how long it has been shaking
during this number of seconds, to decide if it is really moving; default 300
“pmotion_start_percent”: number, if the unit has been shaking at least this percentage
(0-100) of the time over pmotion_start_check_sec seconds, it is moving; default 10; this value
should be greater than pmotion_stop_percent for hysteresis
“pmotion_stop_check_sec”: number, the unit watches how long it has (not) been
shaking during this number of seconds, to decide if it is really stopped; default 300
“pmotion_stop_percent”: number, if the unit has been shaking less than this percentage
(0-100) of the time over pmotion_stop_check_sec seconds, it is stopped; default 5; this value
should be less than pmotion_start_percent for hysteresis
Return:
"Devices": all Devices to which the command was sent, with context and traceability for
the command sent

AssetLink Proprietary Page 26


File: sensor
The actions in this file could be a subset of “remote”, but sensor management is a sufficiently
large topic that it warranted pulling out into its own file. (Largely to allow this contextual
introduction.) However, everything that applies to File:remote, such as the possibility of
incurring over-the-air charges, applies here.

This file is currently oriented toward the AssetPack. If other devices come along with similar
capabilities, we will try to keep the interface normalized, but for now the AssetPack is the only
device in our stable that offers these extensive on-board sensor capabilities.

The AssetPack treats sensors as generators of 16-bit signed numbers, called readings. The
source of these readings may be analog, or Modbus, or GPS speed, or some derived value, or an
application-specific piece of information, but it is always ultimately reduced to a 16-bit signed
number.

As such, its value can be compared against a set of limits, and action taken when it crosses a
limit. The action to be taken — send a message, change operating mode, read another sensor —
is application-specific and beyond the scope of the present document. But the limits themselves
may need to be tuned in the field, and that is what the following Actions are for.

A sensor can be read on demand, or at some regular pace. When read at a regular pace, the
difference between subsequent readings represents a rate of change. This rate can also be
tested against a set of limits.

Sensor limits are defined as follows:

• Black, low. Readings at this value or lower are considered unreal or invalid.
• Red, low. Readings less than this value are considered in the “red” zone.
• Yellow, low. Readings less than this value are considered in the “yellow” zone.
• Green. Readings between the two yellow limits are in the “green” zone.
• Yellow, high. Readings greater than this value are considered in the “yellow” zone.
• Red, high. Readings greater than this value are considered in the “red” zone.
• Black, high. Readings at this value or greater are considered unreal or invalid.
• Hysteresis. Helps avoid toggling across a boundary line.

Zones can be effectively eliminated by setting their limits equal to the zone beyond. So if a
yellow-high zone is not needed, set the yellow-high limit equal to the red-high limit.

Hysteresis is a number which “pushes” the safer boundary further away. That is, once you cross
the yellow-high limit into the yellow zone, the effective yellow-high limit is “pushed away”,
changed to (yellow-high – hysteresis), meaning the sensor value has to drop an extra
“hysteresis” counts in order to be considered safe (green) again. Similarly, if a sensor has

AssetLink Proprietary Page 27


crossed the red-low limit into the red zone, the sensor has to climb past (red-low + hysteresis) in
order to leave the red zone.

Digital sensors are slightly different. Their “readings” can only take on one of four values:

0: Unmonitored
1: Reserved
2: Input fell (went from 1 to 0)
3: Input rose (went from 0 to 1)

The important thing about digital sensors is the time they happened, moreso than their values.
That said, a digital input can be configured to count Pulses Per Minute (PPM) — which is treated,
no surprise, like a 16-bit signed number. Positive values are real PPM measurements; negative
values mean the measurement is not completed. It can have limits associated like any other
sensor. (In this case, the natural value for Black-low, for example, is -1, meaning negative values
are invalid and should be ignored.)

There is only one Action in this file, to make sure all changes are made in one atomic effort, and
there is no chance that a set of sensor readings are taken in the middle of changing limits. As a
result, the data structure is more involved than other Actions.

Action: setLimits
Set limit levels and behavior on one or more sensors.
Access level: 25
Data:
“sensN”: Sensor limits data structure, defined below. N is the number of the sensor. A
single Action can have multiple of these data structures, for different sensors.

For the AssetPack, the following sensors are defined:


sens0: the on-board unit temperature sensor
sens1: input ANA_DIO1, which can be a voltage, current, or digital-input sensor
sens2: input ANA_DIO2, which can be a voltage, current, or digital-input sensor
sens3: digital input sensor representing the availability of external power
sens4: digital input sensor representing tamper switch; or, serial / Modbus sensor
sens5: GPS-derived speed values; or, serial / Modbus sensor
sens6 through sens11: serial / Modbus sensor
Return:
"Devices": all Devices to which the command was sent, with context and traceability for
the command sent

Sensor limits data structure: a combination of the following:


“limits_val”: Limits array, defined below, for the value of the sensor reading
“limits_rate”:1 Limits array, defined below, for the change in the value of the sensor
reading across two consecutive regularly-paced readings

1 limits_rate (for analog sensors that are actively read at some regular pace), custom_divisor and
_subtrahend (esoteric for custom applications), and debounce_ms and calm_sec (for digital input

AssetLink Proprietary Page 28


“custom_divisor”:1 For external serial sensors that report their values as floating-point
numbers, and for which it is inefficient to express scaling information at read time: the value to
divide the floating-point reading by, to make it fit in 16 bits. (As you can tell from the
description, this is an esoteric parameter, used primarily in custom applications.)
“custom_subtrahend”:1 After dividing by custom_divisor, subtract custom_subtrahend,
then record the resulting value as a sensor reading (esoteric; see custom_divisor).
“debounce_ms”: 1 For digital sensors, the amount of time (in milliseconds) that the
input must be consistently in a new state, before the sensor is considered to have truly
transitioned into that state
“calm_sec”:1 Once a digital sensor transition is confirmed, ignore transitions for this
many seconds afterward
“record_skip”: How many samples the unit should take (and test against limits) but not
record. To record all values, record_skip should be zero. If for example a sensor is read every
10s to compare it against limits, but the application only wants a sample every 5 minutes to be
recorded and subsequently transmitted, record_skip would be 29 (skip 29 samples / 290
seconds, record on the 30th / 300 seconds). Range of 0-255.
“flags”: an 8-bit number representing a series of flags with the following meanings:
0b00000001: Normally, the unit only takes action (sends a message, etc.) when a sensor
changes its limit zone: green to yellow, red to green. With this flag set, the unit
takes action every time it finds the sensor value is still in a limit zone.
0b00000010: Same as the previous flag, but for rate instead of value.
0b00000100: Use custom_divisor and custom_subtrahend to scale read values. This sacrifices
the ability to use rate limits.
0b00010000: Usually the “black” limit zone is considered invalid, and values are discarded.
With this flag set, “black”-zone values are still recorded.
0b11100000: A 3-bit number representing how many previous samples must also have been
in the given limit zone, before taking action. Zero means take action as soon as
the sensor arrives in a limit zone. One means take action if two subsequent
readings both show the sensor in the same limit zone. Seven means take action
only if eight readings in a row all show the sensor in the same limit zone.

Limits array:
A limits array always has exactly seven values, in this order:

1. Black, low. Readings at this value or lower are considered unreal or invalid.
2. Red, low. Readings less than this value are considered in the “red” zone.
3. Yellow, low. Readings less than this value are considered in the “yellow” zone.
4. Yellow, high. Readings greater than this value are considered in the “yellow” zone.
5. Red, high. Readings greater than this value are considered in the “red” zone.
6. Black, high. Readings at this value or greater are considered unreal or invalid.
7. Hysteresis. Helps avoid toggling across a boundary line.

It is expressed as a numeric array, with no keys, just the list of seven numbers.

sensors that are transitioning on their own), are all mutually exclusive. A given sensor limits data
structure may have limits_rate, or custom scaling parameters, or debounce_ms and calm_sec, but not
any combination.

AssetLink Proprietary Page 29


This one Action has relatively nested data structures, but as noted, that’s intentional because it
guarantees atomicity for a set of parameter changes. It certainly warrants some examples of its
“data”: contents.

Here is sensor ANA_DIO1, which has analog readings in milliVolts from 0 to 3300, getting a
green zone from 900 to 1800:

"data": {
"sens1": {
"limits_val": [-1,900,900,1800,1800,3700,0]
}
}

Note the yellow and red limits are identical (meaning there is no yellow zone), and the black
limits are set to values that a physical sensor could not produce. Hysteresis is left at zero, so the
limit values are fixed as shown.

Now both ANA_DIO1 and ANA_DIO2 get a full set of limits, with hysteresis; and also the
EXTPWR_IN sensor gets a lot of debounce to deal with noise:

"data": {
"sens1": {
"limits_val": [-1,900,900,1800,1800,3700,5],
"limits_rate": [-32000,-16,-8,8,16,32000,2]
},
"sens2": {
"limits_val": [-1,0,0,1500,2000,3700,10],
"limits_rate": [-32000,-20,-20,20,20,32000,4]
},
"sens3": {
"debounce_ms": 4000
}
}

Note here that “+/-32000” is often used to mean “unrealistically far away”. It is typically best to
have a value like this that is close to the true far limits (-32768 and +32767) but still
encompasses that last edge of extreme values, because often sensors will indeed use full-scale
positive or negative values to represent “invalid” or “unavailable”.

Also important is that any value not changed by this Action, is left at its previous value. So if the
EXTPWR_IN sensor (sens3) had a calm_sec value of 30 seconds, it would remain 30 seconds,
even as the related debounce_ms value is changed.

Finally, here is a set of serial or Modbus sensors that are frequently read for comparison against
limits, but only recorded every 30th time. So if the regular sensor reading pace were 10 seconds,

AssetLink Proprietary Page 30


this would check against limits every 10 seconds, but only record a value every 5 minutes. Also,
a limit violation is only considered real if three readings in a row agree.

"data": {
"sens8": {
"limits_val": [-32000,-190,-170,300,333,32000,10],
"record_skip": 29,
"flags": 64
}
"sens9": {
"limits_val": [-32000,-190,-170,300,333,32000,10],
"record_skip": 29,
"flags": 64
}
"sens10": {
"limits_val": [-32000,-190,-170,300,333,32000,10],
"record_skip": 29,
"flags": 64
}
"sens11": {
"limits_val": [-32000,-190,-170,300,333,32000,10],
"record_skip": 29,
"flags": 64
}
}

Note that flags = 64 = 0b01000000, means the top three bits are 010 = 2, which per the above
definition of the flags value means that a total of three consecutive readings must all agree on
the limit state before action is taken.

Here finally we see that because all of these parameters are updated at once, there is no risk of
these sensors getting out of sync. If these were four separate Actions (even within the same API
request), they would arrive sequentially on the unit, and some might arrive just before a sensor
read and the rest arrive just after, throwing the “every 30th reading” pace out of whack. This is
what warrants the more complicated data structure for this Action.

AssetLink Proprietary Page 31


Cookbook
The following page is a set of the most commonly used API commands. Oftentimes a document like this describes the manifold things a user can do, and the
relatively small number of things a user realistically will do is left as a fatiguing exercise in visual sifting. To aid in getting started, then, we recognize that
(conceptually) 90% of all DeviceManager interactions will be:

1) Log in
2) Get any unit reports that have come in since the last request
3) Log out

… with even #1 and #3 being optional, if you log in once and then maintain that session ID.

On a command line, this might look like (important distinctions in bold):

~$ curl -H "Content-Type: application/json" -X POST -c cookiefile -d '{"USER":{"NAME":"mightyuser","PASSWORD":"kapow"}}'


[Link]

~$ curl -H "Content-Type: application/json" -X POST -b cookiefile -d


'[{"file":"moment","action":"get","filter":"(Moment>18670000)"}]' [Link]

~$ curl -H "Content-Type: application/json" -X POST -b cookiefile -d '{"LOGOUT":true}'


[Link]

Note that the middle step takes advantage of the default limit of 1000 Moments per request.

Most of the remaining 10% of interactions with the DeviceManager, are other API commands as listed on the following page: provisioning units
to allow their data to come through, and requests for a unit to report in as soon as possible. Once your codebase is set up to send these
frequently-used requests, it will be easy to expand into the least-used requests, such as grouping, mode-changing, and naming, described earlier
in this document.

AssetLink Proprietary Page 32


When you want to… Use… For example, Notes
Log in, because this lets you do any of the { "USER" : { {"USER" : {"NAME":"ima_user", This is sent to [Link] instead of
below actions "NAME" : "login_username", "PASSWORD":"Not-a-real-password"}} [Link]. The important part is that this
"PASSWORD" :
interaction starts a session.
"login_password"
}
}
Activate a unit that's about to be deployed [{"file" : "device", [{"file":"device", Drop out the "plan" to use the default billing
"action" : "provision", "action":"provision", "filter":"esn plan.
"filter" : "esn in in ('300234063389400'
('esn1','esn2',...)", '300234063382370',
"data" : { "plan":"7.6kB" } '300234063381390',
}] '300234063371440')"}]
Get the most recent reports from all units2 [{"file" : "moment", [{"file":"moment", This will be subject to the same default 1000-
"action" : "getMostRecent", "action":"getMostRecent"}] Moment-result limit, if not overridden.
}]
Alternately, request specific units using
"filter", or set up Gr[ou]ps on the Manager
and filter for Grps of interest.
Get all of the reports since last time. [{"file" : "moment", [{"file":"moment", "action":"get", Each time you call this action, note the
This is the most common request used over "action" : "get", "filter":"Moment>123456"}] highest Moment id in the response, and use
"filter" : "Moment > last_id_seen"
the API. }] that as the last_id_seen for the next request.
Ids always increase.
Get all of the reports since last time, with a [{"file" : "moment", [{"file":"moment", "action":"get", With this request and the previous one, to get
larger safety limit "action" : "get", "filter":"(Moment>123456)", all the results, keep requesting this (with
"filter" : "Moment > last_id_seen", "limit":2500}]
"limit" : max number of results last_id_seen increasing for each response)
desired until a response with fewer than "limit"
}] Moments are received. (This will also be
indicated by the presence / absence of a
"MomentLimited" element in the response.)
Tell a specific unit to send a position report [{"file" : "remote", [{"file":"remote", Remember that 'remote' commands may
ASAP "action" : "report", "action":"report", incur additional air charges.
"filter" : "esn = 'target_esn'" "filter":"esn='300234063349390'"}]
}]
Deprovision a unit that is no longer in active [{"file" : "device", [{"file":"device", This is primarily for units that will never be
service "action" : "deprovision", "action":"deprovision", reactivated. If a unit is deprovisioned and
"filter" : "esn in "filter":"esn in
('esn1','esn2',...)" ('300234063349390', then provisioned again, additional charges
}] '300234063371440')"}] may result.
Log out, as a safety measure to guard against { "LOGOUT" : true } {"LOGOUT":true} Not necessary, but available for increased
session stealing security. Sessions normally time out after 24
hours. Sent to [Link] instead of
[Link].

2 Only use this when you genuinely want a broad view across all units of their last reports. If you don’t want to miss reports, use the next command in this list.

AssetLink Proprietary Page 33

You might also like