Difference between revisions of "FAQ"
m (→Make the request) |
(→List Devices) |
||
| (23 intermediate revisions by the same user not shown) | |||
| Line 3: | Line 3: | ||
==How do I make REST calls?== | ==How do I make REST calls?== | ||
| + | '''All REST requests should be made against https://rest.sensaphone.net''' | ||
| + | |||
We recommend the use of a REST client for developing and testing calls to our REST API. Searching for 'rest client' in your favorite search engine will turn up many suitable results. | We recommend the use of a REST client for developing and testing calls to our REST API. Searching for 'rest client' in your favorite search engine will turn up many suitable results. | ||
| Line 54: | Line 56: | ||
===Overview=== | ===Overview=== | ||
Queries to our logging facilities are one of the most popular uses of the Sensaphone API. Making a successful log query involves first acquiring multiple pieces of data from the API. Because of this fact, crafting your first log query will be a multi-step process. All logs are accessed through the [[Sensaphone.net_API/history|history resource]]. Outlined below you will find a practical example of how to gather the data needed to make a successful query to the history resource. We will use JSON-mode for each request as we gather the info required to query for datalogs. | Queries to our logging facilities are one of the most popular uses of the Sensaphone API. Making a successful log query involves first acquiring multiple pieces of data from the API. Because of this fact, crafting your first log query will be a multi-step process. All logs are accessed through the [[Sensaphone.net_API/history|history resource]]. Outlined below you will find a practical example of how to gather the data needed to make a successful query to the history resource. We will use JSON-mode for each request as we gather the info required to query for datalogs. | ||
| + | |||
| + | If there are multiple log points within a similar time range you wish to access, it is generally more efficient to submit a request which includes all log points in which you are interested and use larger page sizes. You can then page through the results returned by the API, processing as you go. This is preferable to smaller, more frequent requests to individual log points using smaller page sizes. | ||
===Examples=== | ===Examples=== | ||
| Line 63: | Line 67: | ||
: List of one or more globally unique numeric ID's, each representing a "loggable" device zone. | : List of one or more globally unique numeric ID's, each representing a "loggable" device zone. | ||
; begin_offset | ; begin_offset | ||
| − | : The offset of the first record to be returned to the caller. An offset of 0 indicates the beginning of the queried timerange. Increasing this offset by 50, for example, means we return results beginning with the fiftieth record obtained by the query. Use this value to "page" through results in descending order, newest to oldest. Experiment to find an offset value that works well for your application. | + | : The offset of the first record to be returned to the caller. An offset of 0 indicates the beginning of the queried timerange. Increasing this offset by 50, for example, means we return results beginning with the fiftieth record obtained by the query. Use this value to "page" through results in descending order, newest to oldest. Experiment to find an offset value that works well for your application. Works in tandem with "record_offset". |
; record_offset | ; record_offset | ||
: Number of results to return per "page". | : Number of results to return per "page". | ||
| Line 83: | Line 87: | ||
=====List Devices===== | =====List Devices===== | ||
| − | First let's list all devices associated with our account and choose one from the results. | + | First let's list all devices associated with our account and choose one from the results. To do this we will make a call to the Dashboard resource, requesting the name and device_id of each device on our account. |
<syntaxhighlight lang="JavaScript"> | <syntaxhighlight lang="JavaScript"> | ||
| − | POST https:// | + | POST https://rest.sensaphone.net/api/v1/{12345678}/{1234aaa5678bbbb8765cccc4321dddd}/dashboard |
{ | { | ||
"request_type": "read", | "request_type": "read", | ||
| Line 101: | Line 105: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| − | + | The array of device data can be found in the '''response''' object in the server's reply. For the remainder of this example let's assume we have acquired the following device_id: | |
device_id = 9191 | device_id = 9191 | ||
| Line 108: | Line 112: | ||
<syntaxhighlight lang="JavaScript"> | <syntaxhighlight lang="JavaScript"> | ||
| − | POST https:// | + | POST https://rest.sensaphone.net/api/v1/{12345678}/{1234aaa5678bbbb8765cccc4321dddd}/history/data_log_points |
{ | { | ||
"request_type": "read", | "request_type": "read", | ||
| Line 133: | Line 137: | ||
Calculate the '''start''' and '''end''' timestamps [[#How do I calculate Sensaphone-encoded timestamps?|as outlined here]]. The range of records returned will fall in the range ''(start, end]'', meaning records will be considered a match if the date of the record equals '''end''' all the way up to, but not including, '''start'''. | Calculate the '''start''' and '''end''' timestamps [[#How do I calculate Sensaphone-encoded timestamps?|as outlined here]]. The range of records returned will fall in the range ''(start, end]'', meaning records will be considered a match if the date of the record equals '''end''' all the way up to, but not including, '''start'''. | ||
| − | + | {| class="wikitable" style="border-left: 4px solid #f90; background-color: #fff8e6; width: 100%;" | |
| + | |- | ||
| + | | '''⚠ Warning: <code>start</code> and <code>end</code> are reversed from convention.''' | ||
| + | |||
| + | In the datalog API, <code>start</code> is the '''most recent''' (newest) timestamp in your range, | ||
| + | and <code>end</code> is the '''oldest''' timestamp. Records are returned in descending order, newest first. | ||
| + | The query range is '''exclusive''' of <code>start</code> and '''inclusive''' of <code>end</code>, | ||
| + | expressed as the interval <code>(start, end]</code>. | ||
| + | |} | ||
Let's assume we wish to query the following two dates: | Let's assume we wish to query the following two dates: | ||
| Line 143: | Line 155: | ||
<syntaxhighlight lang="JavaScript"> | <syntaxhighlight lang="JavaScript"> | ||
| − | POST https:// | + | POST https://rest.sensaphone.net/api/v1/{12345678}/{1234aaa5678bbbb8765cccc4321dddd}/history/data_log |
{ | { | ||
"request_type": "read", | "request_type": "read", | ||
| Line 165: | Line 177: | ||
<syntaxhighlight lang="JavaScript" highlight=10> | <syntaxhighlight lang="JavaScript" highlight=10> | ||
| − | POST https:// | + | POST https://rest.sensaphone.net/api/v1/{12345678}/{1234aaa5678bbbb8765cccc4321dddd}/history/data_log |
{ | { | ||
"request_type": "read", | "request_type": "read", | ||
| Line 174: | Line 186: | ||
"start": 671113800, | "start": 671113800, | ||
"end": 669600000, | "end": 669600000, | ||
| − | "begin_offset": | + | "begin_offset": 1000, |
| − | "record_offset": | + | "record_offset": 1000 |
} | } | ||
} | } | ||
| Line 183: | Line 195: | ||
=====Queries against zones of multiple devices===== | =====Queries against zones of multiple devices===== | ||
Recall that zone ID's are unique across '''all''' devices. If you wish to query for datalog records on zones across multiple devices, simply acquire the log points for the zones of the devices you wish to view and make your query as we have shown above. The only difference here is that the log points used in the request represent zones across more than one device. | Recall that zone ID's are unique across '''all''' devices. If you wish to query for datalog records on zones across multiple devices, simply acquire the log points for the zones of the devices you wish to view and make your query as we have shown above. The only difference here is that the log points used in the request represent zones across more than one device. | ||
| + | |||
| + | ===Tips for Effective Datalog Requests=== | ||
| + | # When datalog values for multiple log points are required, an efficient request will include several log points of interest over a given time range with a larger page size (record_offset). This provides more context to the API, allowing it to prefetch records as you page through your results. Avoid making flurries of requests for individual log points and/or using small page sizes where a larger more comprehensive request can be made. | ||
| + | # Adjust your page size (record_offset) to your use case. If performing bulk processing then prefer larger page sizes. Smaller page sizes may be preferable if you are displaying results for users to read. | ||
| + | # Acquire datalog values from multiple devices in the same query if the desired time ranges are compatible. | ||
| + | # Datalog values are not 'live' data. Polling the API repeatedly for recent datalog values will not cause them to appear faster. Datalog records are periodically uploaded to our servers in batches by each device at a set interval. The exact time at which these uploads take place is negotiated between the device and server. | ||
===References=== | ===References=== | ||
| Line 208: | Line 226: | ||
* 2000 - ... the year | * 2000 - ... the year | ||
| − | The algorithm in | + | The algorithm in pseudocode: |
timestamp <-- (seconds mod 60) + // 0 - 59 | timestamp <-- (seconds mod 60) + // 0 - 59 | ||
((minutes * 60) mod 3600) + // 0 - 59 | ((minutes * 60) mod 3600) + // 0 - 59 | ||
| Line 217: | Line 235: | ||
===Implementations=== | ===Implementations=== | ||
| − | + | Provided below are some helpful functions to convert date and time into Sensaphone time stamps. | |
| − | ====Bash==== | + | |
| + | ==== Bash ==== | ||
| + | |||
| + | This implementation accepts a natural language date string and uses the | ||
| + | <code>date</code> program to parse it, handling base-0 conversion internally. | ||
| + | Both GNU <code>date</code> (Linux) and BSD <code>date</code> (macOS) are supported. | ||
This snippet is useful if you are using cURL to make your REST calls from the command line. | This snippet is useful if you are using cURL to make your REST calls from the command line. | ||
| − | <syntaxhighlight lang="bash" line | + | |
| + | <syntaxhighlight lang="bash" line> | ||
sensaphone_time() { | sensaphone_time() { | ||
| − | local | + | local date_string="$1" |
| − | local month=$2 | + | |
| − | local day=$ | + | # Parse date string to Unix timestamp |
| − | local | + | # GNU date (Linux) accepts free-form strings via -d |
| − | local | + | # BSD date (macOS) requires ISO 8601 format: "YYYY-MM-DD HH:MM:SS" |
| − | local | + | if date --version &>/dev/null 2>&1; then |
| + | local unix_time=$(date -d "$date_string" +%s) | ||
| + | else | ||
| + | local unix_time=$(date -j -f "%Y-%m-%d %H:%M:%S" "$date_string" +%s) | ||
| + | fi | ||
| + | |||
| + | # Decompose Unix timestamp into components (day and month adjusted to base-0) | ||
| + | local second=$(date -d "@$unix_time" +%-S 2>/dev/null || date -r "$unix_time" +%-S) | ||
| + | local minute=$(date -d "@$unix_time" +%-M 2>/dev/null || date -r "$unix_time" +%-M) | ||
| + | local hour=$(date -d "@$unix_time" +%-H 2>/dev/null || date -r "$unix_time" +%-H) | ||
| + | local day=$(( $(date -d "@$unix_time" +%-d 2>/dev/null || date -r "$unix_time" +%-d) - 1 )) | ||
| + | local month=$(( $(date -d "@$unix_time" +%-m 2>/dev/null || date -r "$unix_time" +%-m) - 1 )) | ||
| + | local year=$(date -d "@$unix_time" +%Y 2>/dev/null || date -r "$unix_time" +%Y) | ||
| + | |||
| + | local sensaphone_timestamp=$(( (second % 60) + | ||
| + | ((minute * 60) % 3600) + | ||
| + | ((hour * 3600) % 86400) + | ||
| + | ((day * 86400) % 2678400) + | ||
| + | ((month * 2678400) % 32140800) + | ||
| + | ((year % 100) * 32140800) )) | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
echo $sensaphone_timestamp | echo $sensaphone_timestamp | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| + | |||
| + | Example usage: | ||
| + | |||
| + | <syntaxhighlight lang="bash" line> | ||
| + | # GNU date (Linux) - accepts natural language strings | ||
| + | sensaphone_time "November 18 2020 12:30:00" # --> 671113800 | ||
| + | sensaphone_time "2020-11-18 12:30:00" # --> 671113800 | ||
| + | |||
| + | # BSD date (macOS) - requires ISO 8601 format | ||
| + | sensaphone_time "2020-11-18 12:30:00" # --> 671113800 | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | ==== Zsh ==== | ||
| + | This implementation uses the <code>zsh/datetime</code> module's <code>strptime</code> | ||
| + | and <code>strftime</code> builtins to parse and decompose dates without invoking | ||
| + | an external <code>date</code> program. It accepts ISO 8601 format date strings | ||
| + | and handles base-0 conversion internally. | ||
| + | |||
| + | <syntaxhighlight lang="bash" line> | ||
| + | sensaphone_time() { | ||
| + | local date_string="$1" | ||
| + | local unix_time | ||
| + | |||
| + | # Load zsh datetime module for strptime and strftime builtins | ||
| + | zmodload zsh/datetime | ||
| + | |||
| + | # Parse date string to Unix timestamp using the strptime builtin. | ||
| + | # Accepts ISO 8601 format: "YYYY-MM-DD HH:MM:SS" | ||
| + | strptime -s unix_time "%Y-%m-%d %H:%M:%S" "$date_string" | ||
| + | |||
| + | # Decompose Unix timestamp into named components using strftime -s. | ||
| + | # strftime zero-pads values (e.g. "08"), so we use (( x + 0 )) to | ||
| + | # strip leading zeros before arithmetic to avoid octal interpretation. | ||
| + | # Day and month are adjusted to base-0 as required by the API. | ||
| + | local -A t | ||
| + | strftime -s t[second] "%S" $unix_time | ||
| + | strftime -s t[minute] "%M" $unix_time | ||
| + | strftime -s t[hour] "%H" $unix_time | ||
| + | strftime -s t[day] "%d" $unix_time | ||
| + | strftime -s t[month] "%m" $unix_time | ||
| + | strftime -s t[year] "%Y" $unix_time | ||
| + | |||
| + | local second=$(( t[second] + 0 )) | ||
| + | local minute=$(( t[minute] + 0 )) | ||
| + | local hour=$(( t[hour] + 0 )) | ||
| + | local day=$(( t[day] + 0 - 1 )) | ||
| + | local month=$(( t[month] + 0 - 1 )) | ||
| + | local year=$(( t[year] + 0 )) | ||
| + | |||
| + | echo $(( (second % 60) + | ||
| + | ((minute * 60) % 3600) + | ||
| + | ((hour * 3600) % 86400) + | ||
| + | ((day * 86400) % 2678400) + | ||
| + | ((month * 2678400) % 32140800) + | ||
| + | ((year % 100) * 32140800) )) | ||
| + | } | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | Example usage: | ||
| + | |||
| + | <syntaxhighlight lang="bash" line> | ||
| + | # Accepts ISO 8601 format date strings | ||
| + | sensaphone_time "2020-11-18 12:30:00" # --> 671113800 | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | {| class="wikitable" style="border-left: 4px solid #f90; background-color: #fff8e6; width: 100%;" | ||
| + | |- | ||
| + | | '''⚠ Note: <code>zsh/datetime</code> and timezone behavior''' | ||
| + | |||
| + | The <code>zsh/datetime</code> module is included in all standard zsh | ||
| + | distributions and does not require separate installation. As with the other | ||
| + | implementations on this page, dates are interpreted in local time. To use UTC, | ||
| + | set <code>TZ=UTC</code> before calling the function, or export it in your | ||
| + | environment. | ||
| + | |} | ||
====Python==== | ====Python==== | ||
| − | <syntaxhighlight lang=" | + | |
| − | def sensaphone_time( | + | This implementation accepts a standard <code>datetime</code> object. |
| − | return ( | + | Note that Python's <code>datetime.day</code> and <code>datetime.month</code> |
| − | + | are both 1-indexed, so both are adjusted to base-0 internally. | |
| − | + | ||
| − | + | <syntaxhighlight lang="python" line> | |
| − | + | from datetime import datetime | |
| − | + | ||
| + | def sensaphone_time(dt): | ||
| + | return (dt.second % 60) + \ | ||
| + | ( (dt.minute * 60) % 3600) + \ | ||
| + | ( (dt.hour * 3600) % 86400) + \ | ||
| + | (((dt.day - 1) * 86400) % 2678400) + \ | ||
| + | (((dt.month - 1) * 2678400) % 32140800) + \ | ||
| + | ( (dt.year % 100) * 32140800) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| − | ====PHP==== | + | Example usage: |
| − | <syntaxhighlight lang=" | + | |
| + | <syntaxhighlight lang="python" line> | ||
| + | # November 18, 2020 12:30pm (local time) | ||
| + | dt = datetime(2020, 11, 18, 12, 30, 0) | ||
| + | print(sensaphone_time(dt)) # --> 671113800 | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | ==== PHP ==== | ||
| + | |||
| + | This implementation accepts a standard <code>DateTime</code> object. | ||
| + | PHP's <code>DateTime</code> uses 1-indexed days and months, so both | ||
| + | are adjusted to base-0 internally. | ||
| + | |||
| + | <syntaxhighlight lang="php" line> | ||
<?php | <?php | ||
| − | function | + | function sensaphone_time(DateTime $dt): int { |
| − | return ( $ | + | $second = (int)$dt->format('s'); |
| − | + | $minute = (int)$dt->format('i'); | |
| − | + | $hour = (int)$dt->format('G'); | |
| − | + | $day = (int)$dt->format('j') - 1; | |
| − | + | $month = (int)$dt->format('n') - 1; | |
| − | + | $year = (int)$dt->format('Y'); | |
| + | |||
| + | return ($second % 60) + | ||
| + | (($minute * 60) % 3600) + | ||
| + | (($hour * 3600) % 86400) + | ||
| + | (($day * 86400) % 2678400) + | ||
| + | (($month * 2678400) % 32140800) + | ||
| + | (($year % 100) * 32140800); | ||
} | } | ||
| + | ?> | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | Example usage: | ||
| + | |||
| + | <syntaxhighlight lang="php" line> | ||
| + | <?php | ||
| + | // November 18, 2020 12:30pm (local time) | ||
| + | $dt = new DateTime('2020-11-18 12:30:00'); | ||
| + | echo sensaphone_time($dt); // --> 671113800 | ||
?> | ?> | ||
</syntaxhighlight> | </syntaxhighlight> | ||
====JavaScript==== | ====JavaScript==== | ||
| + | |||
| + | This implementation accepts a standard JavaScript <code>Date</code> object, | ||
| + | handling the base-0 conversion for day-of-month internally. It is compatible | ||
| + | with both browser environments and Node.js. | ||
| + | |||
<syntaxhighlight lang="JavaScript" line> | <syntaxhighlight lang="JavaScript" line> | ||
| − | function sensaphoneTime( | + | function sensaphoneTime(date) { |
| − | return ( | + | return ( date.getSeconds() % 60) + |
| − | (( | + | ( (date.getMinutes() * 60) % 3600) + |
| − | (( | + | ( (date.getHours() * 3600) % 86400) + |
| − | (( | + | (((date.getDate() - 1) * 86400) % 2678400) + // Note: We must subtract 1! |
| − | (( | + | ( (date.getMonth() * 2678400) % 32140800) + |
| − | (( | + | ( (date.getFullYear() % 100) * 32140800); |
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| − | + | Example usage: | |
| − | + | ||
| − | + | <syntaxhighlight lang="javascript" line> | |
| − | + | // November 18, 2020 12:30pm (local time) | |
| − | <syntaxhighlight lang=" | + | const date = new Date(2020, 10, 18, 12, 30, 0); |
| − | + | sensaphoneTime(date); // --> 671113800 | |
| − | + | ||
| − | + | ||
| − | + | ||
| − | // November | + | |
| − | + | ||
| − | + | ||
| − | + | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| − | |||
| − | |||
| − | |||
===References=== | ===References=== | ||
* [[Sensaphone.net API/history#Timestamps|Sensaphone-encoded timestamps]]: More info regarding timestamp encoding | * [[Sensaphone.net API/history#Timestamps|Sensaphone-encoded timestamps]]: More info regarding timestamp encoding | ||
* [https://en.wikipedia.org/wiki/Modulo_operation Modulo (mod) operation]: Information about the modulo operation. | * [https://en.wikipedia.org/wiki/Modulo_operation Modulo (mod) operation]: Information about the modulo operation. | ||
Latest revision as of 11:58, 20 April 2026
Contents
Where's the API Documentation?
How do I make REST calls?
All REST requests should be made against https://rest.sensaphone.net
We recommend the use of a REST client for developing and testing calls to our REST API. Searching for 'rest client' in your favorite search engine will turn up many suitable results.
How do I login?
Overview
To use the Sensaphone REST API, a login call must first be made. A successful login request will return an account number and a session token. These will be used in all subsequent requests. Two examples of login requests are shown below. First, the URI-mode version, followed by the JSON-mode version.
Examples
URI-mode:
POST https://rest.sensaphone.net/api/v1/login/{[email protected]}/{abc123_my_password}
JSON-mode:
POST https://rest.sensaphone.net/api/v1/login { "request_type": "create", "resource": "login", "user_name": "[email protected]", "password": "abc123_my_password" }
Reply
The reply to a successful login request will contain a result object and a response object. The result communicates the overall success of the call. The response contains the data in which we are interested: in this case, the account number (acctid), and the session token. The account number and session token will be used in every subsequent query submitted to the API.
{ "result": { "success": true, "code": 0, "message": "Success" }, "response": { "acctid": 21620750, "session": "1234ffffeeee5678aaaccda609cd8fb5099", "login_timestamp": 1605562465, "session_expiration": 86400, "user_id": 12345678 } }
References
- Login Resource: Additional information about the login resource
- URI-mode/JSON-mode: Information about URI-mode and JSON-mode
How do I access log data?
Overview
Queries to our logging facilities are one of the most popular uses of the Sensaphone API. Making a successful log query involves first acquiring multiple pieces of data from the API. Because of this fact, crafting your first log query will be a multi-step process. All logs are accessed through the history resource. Outlined below you will find a practical example of how to gather the data needed to make a successful query to the history resource. We will use JSON-mode for each request as we gather the info required to query for datalogs.
If there are multiple log points within a similar time range you wish to access, it is generally more efficient to submit a request which includes all log points in which you are interested and use larger page sizes. You can then page through the results returned by the API, processing as you go. This is preferable to smaller, more frequent requests to individual log points using smaller page sizes.
Examples
Datalog
Every device (e.g. Sentinel, Sentinel Pro, Stratus) associated with an account has input zones to which sensors may be connected. A device may be configured to log the value of any input zone at specific intervals. On www.sensaphone.net (the Website) the user may query a time range of datalog records for a single device. Users of the REST API can make similar queries against the input zones of one or more devices.
The data required to perform a datalog query are defined as follows:
- log_points/data_log_points
- List of one or more globally unique numeric ID's, each representing a "loggable" device zone.
- begin_offset
- The offset of the first record to be returned to the caller. An offset of 0 indicates the beginning of the queried timerange. Increasing this offset by 50, for example, means we return results beginning with the fiftieth record obtained by the query. Use this value to "page" through results in descending order, newest to oldest. Experiment to find an offset value that works well for your application. Works in tandem with "record_offset".
- record_offset
- Number of results to return per "page".
- start
- The greatest (most recent) Sensaphone-encoded timestamp we are interested in, non-inclusive. Also see below for more details about how this value may be calculated.
- end
- The least (oldest) Sensaphone-encoded timestamp we are interested in, inclusive. Also see below for more details about how this value may be calculated.
We will take the following steps to retrieve the information necessary for a query to the datalog facility:
- Get a list of devices associated with an account
- Get a list of log_points from a device
- Calculate Sensaphone-encoded start and end timestamps
- Query for datalog records
- Page through the results
For the remainder of this example, let's assume the following:
acctid = 12345678 # account_id session = 1234aaa5678bbbb8765cccc4321dddd # session token
List Devices
First let's list all devices associated with our account and choose one from the results. To do this we will make a call to the Dashboard resource, requesting the name and device_id of each device on our account.
POST https://rest.sensaphone.net/api/v1/{12345678}/{1234aaa5678bbbb8765cccc4321dddd}/dashboard { "request_type": "read", "resource": "dashboard", "dashboard": { "device": [ { "device_id": null, "name": null } ] } }
The array of device data can be found in the response object in the server's reply. For the remainder of this example let's assume we have acquired the following device_id:
device_id = 9191
List Log Points
With a device_id of 9191 in hand, we may now query for log_points associated with that device.
POST https://rest.sensaphone.net/api/v1/{12345678}/{1234aaa5678bbbb8765cccc4321dddd}/history/data_log_points { "request_type": "read", "resource": "history", "history": [ { "data_log_points": { "resource_type": "device", "device_id": 9191 } } ] }
The server's reply will contain data for each loggable zone, including input zones and output zones. The log_point value of each zone is unique across all devices. Therefore a log_point value of 78208181 will refer to one, and only one, zone. Extract log_point values for all zones to be queried.
Assume we have decided to pull datalog records for the following log points:
15144093 15145683 15148853
Calculate start and end timestamps
Calculate the start and end timestamps as outlined here. The range of records returned will fall in the range (start, end], meaning records will be considered a match if the date of the record equals end all the way up to, but not including, start.
⚠ Warning: start and end are reversed from convention.
In the datalog API, |
Let's assume we wish to query the following two dates:
start = November 18, 2020 12:30pm --> sensaphone_time(2020, 10, 17, 12, 30, 00) --> 671113800 end = November 01, 2020 12:00am --> sensaphone_time(2020, 10, 00, 00, 00, 00) --> 669600000
Make the request
Now that we have all the necessary data we can put it together and make our datalog request.
POST https://rest.sensaphone.net/api/v1/{12345678}/{1234aaa5678bbbb8765cccc4321dddd}/history/data_log { "request_type": "read", "resource": "history", "history": { "data_log": { "log_points": [ 15144093, 15145683, 15148853 ], "start": 671113800, "end": 669600000, "begin_offset": 0, "record_offset": 1000 } } }
Assuming records exist for those log_points during that time range, 1000 of them will be returned. Why 1000? Recall that record_offset determines the maximum number of records to be returned per request.
Paging through results
If there are more records to be reviewed, we can page through our results by making another call. This time we will increment the begin_offset by the number of records per page (record_offset).
POST https://rest.sensaphone.net/api/v1/{12345678}/{1234aaa5678bbbb8765cccc4321dddd}/history/data_log { "request_type": "read", "resource": "history", "history": { "data_log": { "log_points": [ 15144093, 15145683, 15148853 ], "start": 671113800, "end": 669600000, "begin_offset": 1000, "record_offset": 1000 } } }
Queries against zones of multiple devices
Recall that zone ID's are unique across all devices. If you wish to query for datalog records on zones across multiple devices, simply acquire the log points for the zones of the devices you wish to view and make your query as we have shown above. The only difference here is that the log points used in the request represent zones across more than one device.
Tips for Effective Datalog Requests
- When datalog values for multiple log points are required, an efficient request will include several log points of interest over a given time range with a larger page size (record_offset). This provides more context to the API, allowing it to prefetch records as you page through your results. Avoid making flurries of requests for individual log points and/or using small page sizes where a larger more comprehensive request can be made.
- Adjust your page size (record_offset) to your use case. If performing bulk processing then prefer larger page sizes. Smaller page sizes may be preferable if you are displaying results for users to read.
- Acquire datalog values from multiple devices in the same query if the desired time ranges are compatible.
- Datalog values are not 'live' data. Polling the API repeatedly for recent datalog values will not cause them to appear faster. Datalog records are periodically uploaded to our servers in batches by each device at a set interval. The exact time at which these uploads take place is negotiated between the device and server.
References
- History: Documentation for the history resource.
- Device: Documentation for the device resource.
- Sensaphone Timestamps: Documentation for Sensaphone-encoded timestamps.
- Data Log Points: Documentation for the data_log_points resource.
- Modulo (mod) operation: Information about the modulo operation.
How do I calculate Sensaphone-encoded timestamps?
Overview
Internally, the Sensaphone API uses a custom date encoding. The only time a user of the REST API must worry about these encoded timestamps is when making requests against the history resource.
The most important thing to keep in mind when converting to Sensaphone timestamps: All data passed into the algorithm is base-0. This means that the first day of the month is not 1, but 0. Likewise January is not 1, but 0. Due to the fact that we count our minutes and seconds from 0, you will not notice a difference when passing these values into the algorithm. The same goes for the year. Where this base-0 concept will have the most noticeable impact will be hour of the day, day of the month, and month of the year.
Observe:
- 0 - 59 seconds per minute
- 0 - 59 minutes per hour
- 0 - 23 hours per day (0 = 12am, 1, 2, ..., 23 = 11pm)
- 0 - 30 are 31-day months
- 0 - 29 are 30-day months
- 0 - 27 most Februaries
- 0 - 28 leap-year Februaries
- 0 - 11 months per year
- 2000 - ... the year
The algorithm in pseudocode:
timestamp <-- (seconds mod 60) + // 0 - 59
((minutes * 60) mod 3600) + // 0 - 59
((hours * 3600) mod 86400) + // 0 - 23
((day * 86400) mod 2678400) + // 0 - 31
((month * 2678400) mod 32140800) + // 0 - 11
((year mod 100) * 32140800) // 2000 - ...
Implementations
Provided below are some helpful functions to convert date and time into Sensaphone time stamps.
Bash
This implementation accepts a natural language date string and uses the
date program to parse it, handling base-0 conversion internally.
Both GNU date (Linux) and BSD date (macOS) are supported.
This snippet is useful if you are using cURL to make your REST calls from the command line.
sensaphone_time() {
local date_string="$1"
# Parse date string to Unix timestamp# GNU date (Linux) accepts free-form strings via -d# BSD date (macOS) requires ISO 8601 format: "YYYY-MM-DD HH:MM:SS"if date --version &>/dev/null 2>&1; then
local unix_time=$(date -d "$date_string" +%s)
elselocal unix_time=$(date -j -f "%Y-%m-%d %H:%M:%S" "$date_string" +%s)
fi# Decompose Unix timestamp into components (day and month adjusted to base-0)local second=$(date -d "@$unix_time" +%-S 2>/dev/null || date -r "$unix_time" +%-S)
local minute=$(date -d "@$unix_time" +%-M 2>/dev/null || date -r "$unix_time" +%-M)
local hour=$(date -d "@$unix_time" +%-H 2>/dev/null || date -r "$unix_time" +%-H)
local day=$(( $(date -d "@$unix_time" +%-d 2>/dev/null || date -r "$unix_time" +%-d) - 1 ))
local month=$(( $(date -d "@$unix_time" +%-m 2>/dev/null || date -r "$unix_time" +%-m) - 1 ))
local year=$(date -d "@$unix_time" +%Y 2>/dev/null || date -r "$unix_time" +%Y)
local sensaphone_timestamp=$(( (second % 60) +
((minute * 60) % 3600) +
((hour * 3600) % 86400) +
((day * 86400) % 2678400) +
((month * 2678400) % 32140800) +
((year % 100) * 32140800) ))
echo $sensaphone_timestamp
}
Example usage:
# GNU date (Linux) - accepts natural language stringssensaphone_time "November 18 2020 12:30:00" # --> 671113800
sensaphone_time "2020-11-18 12:30:00" # --> 671113800
# BSD date (macOS) - requires ISO 8601 formatsensaphone_time "2020-11-18 12:30:00" # --> 671113800
Zsh
This implementation uses the zsh/datetime module's strptime
and strftime builtins to parse and decompose dates without invoking
an external date program. It accepts ISO 8601 format date strings
and handles base-0 conversion internally.
sensaphone_time() {
local date_string="$1"
local unix_time# Load zsh datetime module for strptime and strftime builtinszmodload zsh/datetime# Parse date string to Unix timestamp using the strptime builtin.# Accepts ISO 8601 format: "YYYY-MM-DD HH:MM:SS"strptime -s unix_time "%Y-%m-%d %H:%M:%S" "$date_string"
# Decompose Unix timestamp into named components using strftime -s.# strftime zero-pads values (e.g. "08"), so we use (( x + 0 )) to# strip leading zeros before arithmetic to avoid octal interpretation.# Day and month are adjusted to base-0 as required by the API.local -A t
strftime -s t[second] "%S" $unix_time
strftime -s t[minute] "%M" $unix_time
strftime -s t[hour] "%H" $unix_time
strftime -s t[day] "%d" $unix_time
strftime -s t[month] "%m" $unix_time
strftime -s t[year] "%Y" $unix_time
local second=$(( t[second] + 0 ))
local minute=$(( t[minute] + 0 ))
local hour=$(( t[hour] + 0 ))
local day=$(( t[day] + 0 - 1 ))
local month=$(( t[month] + 0 - 1 ))
local year=$(( t[year] + 0 ))
echo $(( (second % 60) +
((minute * 60) % 3600) +
((hour * 3600) % 86400) +
((day * 86400) % 2678400) +
((month * 2678400) % 32140800) +
((year % 100) * 32140800) ))
}
Example usage:
# Accepts ISO 8601 format date stringssensaphone_time "2020-11-18 12:30:00" # --> 671113800
⚠ Note: zsh/datetime and timezone behavior
The |
Python
This implementation accepts a standard datetime object.
Note that Python's datetime.day and datetime.month
are both 1-indexed, so both are adjusted to base-0 internally.
from datetime import datetime
def sensaphone_time(dt):
return (dt.second % 60) + \
( (dt.minute * 60) % 3600) + \
( (dt.hour * 3600) % 86400) + \
(((dt.day - 1) * 86400) % 2678400) + \
(((dt.month - 1) * 2678400) % 32140800) + \
( (dt.year % 100) * 32140800)
Example usage:
# November 18, 2020 12:30pm (local time)dt = datetime(2020, 11, 18, 12, 30, 0)
print(sensaphone_time(dt)) # --> 671113800
PHP
This implementation accepts a standard DateTime object.
PHP's DateTime uses 1-indexed days and months, so both
are adjusted to base-0 internally.
<?phpfunction sensaphone_time(DateTime $dt): int {
$second = (int)$dt->format('s');
$minute = (int)$dt->format('i');
$hour = (int)$dt->format('G');
$day = (int)$dt->format('j') - 1;
$month = (int)$dt->format('n') - 1;
$year = (int)$dt->format('Y');
return ($second % 60) +
(($minute * 60) % 3600) +
(($hour * 3600) % 86400) +
(($day * 86400) % 2678400) +
(($month * 2678400) % 32140800) +
(($year % 100) * 32140800);
}?>
Example usage:
<?php// November 18, 2020 12:30pm (local time)$dt = new DateTime('2020-11-18 12:30:00');
echo sensaphone_time($dt); // --> 671113800
?>
JavaScript
This implementation accepts a standard JavaScript Date object,
handling the base-0 conversion for day-of-month internally. It is compatible
with both browser environments and Node.js.
function sensaphoneTime(date) {
return ( date.getSeconds() % 60) +
( (date.getMinutes() * 60) % 3600) +
( (date.getHours() * 3600) % 86400) +
(((date.getDate() - 1) * 86400) % 2678400) + // Note: We must subtract 1!
( (date.getMonth() * 2678400) % 32140800) +
( (date.getFullYear() % 100) * 32140800);
}
Example usage:
// November 18, 2020 12:30pm (local time)const date = new Date(2020, 10, 18, 12, 30, 0);
sensaphoneTime(date); // --> 671113800
References
- Sensaphone-encoded timestamps: More info regarding timestamp encoding
- Modulo (mod) operation: Information about the modulo operation.