CPs can work with timestamps as well as values, and even see the previous event in a stream
CPs can access the previous state of any property, and even the time when that state changed, using the $previous keyword.
For example, if we’re ingesting data into DevicePilot via a connection-oriented protocol such as MQTT then instead of heartbeats we’ll receive a simple "connected" property which flips between true each time the connection starts and false each time it stops. We can use $previous to detect the moment that the connection starts:
connected == true && $previous/connected == false
Converting state ←→ events
We can think of the above CP as turning a state property into an event property and now we can COUNT became_connected in a KPI to measure how many sessions started e.g. in a day.
We can also go the other way and turn event properties into state properties. Say our device emits two event properties: cashbox_opened and cashbox_closed (it doesn’t matter what value it sets these properties to, each event is defined as just sending the property with some value). We can define a CP thus:
$ts/cashbox_opened > $ts/cashbox_closed
The cashbox is open if it was opened more recently than it was closed. Now in a KPI we can measure e.g. percentage_of_time_where cashbox_is_open.
In a similar vein, you can use CP’s to merge event streams. If your device reports a variety of device errors on different properties, you can merge them into a single property for counting or other statistics:
tamper_detect || battery_low || sensor_fail
By accessing the previous timestamp, we can report statistics such as:
connected == true && $previous/connected == false ?
$ts/connected - $previous/$ts/connected : undefined
In English this says "if we’re currently connected and were previously unconnected, then report the elapsed time since we became connected. Otherwise report nothing".
ASIDE: To better understand the difference between "null" and "undefined" values, see here.
Many machines have an in-built "hours of operation" odometer. Even if we don't have remote access to this, we can still calculate it from any reported property from which operation can be inferred - for example when a fryer is up to operating temperature:
((fryer_temp > 300) && ($previous/fryer_temp > 300)) ? (($ts/fryer_temp - $previous/$ts/fryer_temp) / (1000 * 60 * 60)) : 0
This can then be summed in a KPI to provide total operating machine-hours per month, or per location etc.
Splitting time into components
You can use the following functions to get the components of a timestamp:
- getSecond - gets the number of seconds: 0 - 59
- getMinute - gets the number of minutes: 0 - 59
- getHour - gets the hour in day: 0 - 23
- getDay - gets day in the year: 0 - 366
- getWeek - gets ISO week: 0 - 53
- getMonth - gets month: 0 - 11 - Months start at zero, so January is month 0.
- getYear - gets year: 2019
- getDayOfWeek - gets day in week: 0 = Sunday, 6 = Saturday
- getDayOfMonth - gets day in the month: 1 - 31
They are used like this (to create a basic business hours filter of 9am-6pm, Mon-Fri):
getHour($ts) >= 9 && getHour($ts) < 18 && getDayOfWeek($ts) > 0 && getDayOfWeek($ts) < 6
These functions all work in UTC. If you want to work in a different timezone, you can use versions with suffixed with Tz, for example getHourTz, and pass the timezone string, from the IANA time zone database.
For a Greek customer, they would specify:
getHourTz($ts, "Europe/Athens") >= 9 && getHour($ts, "Europe/Athens") < 18 &&
getDayOfWeek($ts, "Europe/Athens") > 0 && getDayOfWeek($ts, "Europe/Athens") < 6