DeviceManager API Guide
DeviceManager API Guide
DeviceManager
Web API
Revision 4, September 2020
[Link]
service@[Link]
+1 (303) 862.8745
AssetLink Proprietary
Revision History
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.
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
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" : {}
} ]
[ { "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.
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>)
<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
}
<special> ∈ {
NULL
we may add other special values for the DeviceManager to
interpret
}
Grps above, specifying the Grps that matching Devices must be a leaf of; plus
Devices above, specifying the Devices whose Moments we are interested in; plus
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.
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:
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"
}
}
{ "USER" : {
"NAME" : "login_username",
"STATUS" : "VERIFIED"
}
}
on success, or
{ "USER" : {
"NAME" : "login_username",
"STATUS" : "UNVERIFIED"
}
}
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.
• 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.
{ "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" }
] },
{ "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" }
] }
] }
{ "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,
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:
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.
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
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.
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.
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
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:
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.
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
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.
• 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
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.
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
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.
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,
"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.
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.
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.
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.