Plain rules

Plain rules allow a full customization of a rule with specific needs by means of setting the final EPL statement used by the Esper engine inside perseo-core. In order to work with perseo (front-end) properly, the EPL statement must fulfill several conventions for the rule to be able to operate on the incoming events and trigger adequate actions.

The “anatomy” of a rule is as follows

{
    "name": "blood_rule_update",
    "text": "select *, *, ev.BloodPressure? as Pressure, ev.id? as Meter from pattern [every ev=iotEvent(cast(cast(BloodPressure?,String),float)>1.5 and type=\"BloodMeter\")]",
    "action": {
        "type": "update",
        "parameters": {
            "name": "abnormal",
            "value": "true",
            "type": "boolean"
        }
    }
}

The fields (all must be present) are

  • name: name of the rule, used as identifier
  • text: EPL statement for the rule engine in perseo-core
  • action: action to be performed by perseo if the rule is fired from the core

The rule name must consist of the ASCII characters from A to Z, from a to z, digits (0-9), underscore (_) and dash (-). It can have a maximum length of 50 characters.

The field action can be also an array of "actions", objects with the same structure than the single action described in the rest of the documentation. Each of those actions will be executed when the rule is fired, avoiding to duplicate a rule only for getting several actions executed. For practical purposes, it is the same result that would be obtained with multiple rules with the same condition.

EPL text

The field text of the rule must be a valid EPL statement and additionally must honor several restrictions to match expectations of perseo and perseo-core.

EPL is documented in Esper site, in particular version 6.1.0.

A EPL statement to use with perseo could be:

select *, ev.BloodPressure? as Pressure, ev.id? as Meter
from pattern
    [every ev=iotEvent(cast(cast(BloodPressure?,String),float)>1.5 and type="BloodMeter")]
  • The from pattern must name the event as ev and the event stream from which take events must be iotEvent
  • A type= condition must be concatenated for avoiding mixing different kinds of entities
  • The variable 'ruleName' in automatically added to the action, even if it is not present in the EPL text. The ruleName automatically added this way is retrieved as part of the EPL text when the rule is recovered using GET /rules or GET /rules/{name}.

Backward compatibility note: since version 1.8.0 it is not mandatory to specify the name of the rule as part of the EPL text. In fact, it is not recommendable to do that. However, for backward compatibility, it can be present as ruleName alias (e.g: select *, "blood_rule_update" as ruleName...) in the select clause. If present, it must be equal to the ‘name’ field of the rule object.

The used entity's attributes must be cast to float in case of being numeric (like in the example). Alphanumeric values must be cast to String. Nested cast to string and to float is something we are analyzing, and could be unnecessary in a future version. Use it by now. All the attributes in the notification from Orion are available in the event object, ev, like ev.BlodPressure? and ev.id?. A question mark is necessary for EPL referring ‘dynamic’ values. Metadata is also available as explained in Metadata and object values.

Actions

When a rule is fired, the "complex event" generated by the select clause is sent to perseo (front-end) which executes the action using the generated event as parameter to the action.

There are a predefined set of actions: sending a SMS, sending a email, updating an attribute of the entity that fired the rule and making a HTTP POST to a provided URL.

The action must be provided in the action field of rule. An example:

   "action":{
      "type":"update",
      "parameters":{
         "name":"abnormal",
         "value":"true",
         "type":"boolean"
      },
      "interval" : "30e3"
   }

The type field is mandatory and must be one of

  • update: update an entity's attribute
  • sms: send a SMS
  • email: send an email
  • post: make an HTTP POST
  • twitter: send a twitter

An action can optionally have a field interval for limiting the frequency of the action execution (for the rule and entity which fired it). The value is expressed in milliseconds and is the minimum period between executions. Once the action is executed successfully, it won't be executed again until that period has elapsed. All the request from core to execute it are silently discarded. This way, the rate of executions for an entity and rule can be limited. (Strictly, the minimum time between executions)

String substitution syntax

Some of the fields of an action (see detailed list below) can include a reference to one of the fields of the notification/event. This allows include information as the received "pressure" value, the ID of the device, etc. For example, the actions sms, email, post include a field template used to build the body of message/request. This text can include placeholders for attributes of the generated event. That placeholder has the form ${X} where X may be:

  • id for the ID of the entity that triggers the rule.
  • type for the type of the entity that triggers the rule
  • Any other value is interpreted as the name of an attribute in the entity which triggers the rule and the placeholder is substituted by the value of that attribute.

All alias for simple event attributes or "complex" calculated values can be directly used in the placeholder with their name. And any of the original event attributes (with the special cases for id and type meaning entity ID and type, respectively) can be referred too.

This substitution can be used in the following fields:

  • template, from, to and subject for email action
  • template, url, qs, headers, json for post action
  • template for sms action
  • template for twitter action
  • id, type, name, value, ìsPattern for update action

SMS action

Sends a SMS to a number set as an action parameter with the body of the message built from the template

 "action": {
        "type": "sms",
        "template": "Meter ${Meter} has pressure ${Pressure}.",
        "parameters": {
            "to": "123456789"
        }
    }

The field parameters include a field to with the number, or numbers separated by whiestpace charaters, to send the message to.

The template and to fields perform attribute substitution.

email action

Sends an email to the recipient set in the action parameters, with the body mail build from the template field. A field to in parameters sets the recipient and a field fromsets the sender's email address. Also the subject of the email can be set in the field subject in parameters.

 "action": {
        "type": "email",
        "template": "Meter ${Meter} has pressure ${Pressure} (GEN RULE)",
        "parameters": {
            "to": "someone@telefonica.com",
            "from": "cep@system.org",
            "subject": "It's The End Of The World As We Know It (And I Feel Fine)"
        }
    }

The template, from, to and subject fields perform string substitution.

update attribute action

Updates one or more attributes of a given entity (in the Context Broker instance specified in the Perseo configuration). The parameters map includes the following fields:

  • id: optional, the ID of the entity which attribute is to be updated (by default the ID of the entity that triggers the rule is used, i.e. ${id})
  • type: optional, the type of the entity which attribute is to be updated (by default the type of the entity that triggers the rule is usedi.e. ${type})
  • version: optional, The NGSI version for the update action. Set this attribute to 2 or "2" if you want to use NGSv2 format. 1 by default
  • isPattern: optional, false by default. (Only for NGSIv1. If version is set to 2, this attribute will be ignored)
  • attributes: mandatory, array of target attributes to update. Each element of the array must contain the fields
    • name: mandatory, attribute name to set
    • value: mandatory, attribute value to set
    • type: optional, type of the attribute to set. By default, not set (in which case, only the attribute value is changed).
  • actionType: optional, type of CB action: APPEND or UPDATE. By default is APPEND.
  • trust: optional, trust token for getting an access token from Auth Server which can be used to get to a Context Broker behind a PEP.

NGSIv1 example:

"action":{
        "type":"update",
        "parameters":{
            "id":"${id}_mirror",
            "attributes": [
                {
                    "name":"abnormal",
                    "type":"boolean",
                    "value":"true"
                }
            ],
            "actionType": "UPDATE"
        }
    }

The name parameter cannot take id or type as a value, as that would refer to the entity's ID and the entity's type (which are not updatable) and not to an attribute with any of those names. Trying to create such action will return an error.

The id, type, name, value, ìsPattern fields perform string substitution.

First time an update action using trust token is triggered, Perseo interacts with Keystone to get the temporal auth token corresponding to that trust token. This auth token is cached and used in every new update associated to the same action. Eventually, Perseo can receive a 401 Not Authorized due to auth token expiration. In that case, Perseo interacts again with Keystone to get a fresh auth token, then retries the update that causes the 401 (and the cache is refreshed with the new auth token for next updates).

It could happen (in theory) that a just got auth token also produce a 401 Not authorized, however this would be an abnormal situation: Perseo logs the problem with the update but doesn't try to get a new one from Keystone. Next time Perseo triggers the action, the process may repeat, i.e. first update attempt fails with 401, Perseo requests a fresh auth token to Keystone, the second update attempt fails with 401, Perseo logs the problem and doesn't retry again.

NGSIv2 example:

"action":{
        "type":"update",
        "parameters":{
            "id":"${id}_mirror",
            "version": 2,
            "attributes": [
                {
                    "name":"abnormal",
                    "type":"Number",
                    "value": 7
                }
            ]
        }
    }

Note: NGSIv2 update actions ignore the trust token for now.

When using NGSIv2 in the update actions, the value field perform string substitution. If value is a String, Perseo will parse the value taking into account the type field, this only applies to Number, Boolean and None types.

Data Types for NGSIv2:

With Number type attributes, Perseo can be able to manage a int/float number or a String to parse in value field.

  • Number from variable:
{
    "name": "numberFromValue",
    "type": "Number",
    "value": "${NumberValue}"
}

If NumberValue value is for example 32.12, this attribute will take 32.12 as value.

  • Literal Number:
{
    "name": "numberLiteral",
    "type": "Number",
    "value": 12
}

This attribute will take 12 as value.

  • Number as String from variable:
{
    "name": "numberFromStringValue",
    "type": "Number",
    "value": "${NumberValueAsString}"
}

If NumberValueAsString value is for example "4.67", this attribute will take 4.67 as value.

  • Number as String:
{
    "name": "numberStringLiteral",
    "type": "Number",
    "value": "67.8"
}

This attribute will take 67.8 as value.

With Text type attributes, Perseo will put the value field parsed as string.

  • Text as variable:
{
    "name": "textFromValue",
    "type": "Text",
    "value": "${varValue}"
}

If varValue value is for example "Good morning", this attribute will take "Good morning" as value.

If varValue value is for example 1234, this attribute will take "1234" as value.

  • Literal Text:
{
    "name": "textLiteral",
    "type": "Text",
    "value": "Hello world"
}

This attribute will take "Hello world" as value.

  • Literal Number:
{
    "name": "textNumberLiteral",
    "type": "Text",
    "value": 67.8
}

This attribute will take "67.8" as value.

  • Literal Boolean:
{
    "name": "textBoolLiteral",
    "type": "Text",
    "value": true
}

This attribute will take "true" as value.

With DateTime type attributes, Perseo will try to parse the value to DateTime format.

Date as String:

{
    "name": "dateString",
    "type": "DateTime",
    "value": "2018-12-05T11:31:39.00Z"
}

This attribute will take "2018-12-05T11:31:39.000Z" as value.

Date as Number in milliseconds:

{
    "name": "dateString",
    "type": "DateTime",
    "value": 1548843229832
}

This attribute will take "2019-01-30T10:13:49.832Z" as value.

Date from variable.

{
    "name": "dateString",
    "type": "DateTime",
    "value": "${dateVar}"
}

If dateVar value is for example 1548843229832 (as Number or String), this attribute will take "2019-01-30T10:13:49.832Z" as value.

You can use the __ts field of a Perseo DateTime attribute to fill a DateTime attribute value without using any cast(). For example, if the var are defined as follow in the rule text, ev.timestamp__ts? as dateVar, dateVar will be a String with the Date in milliseconds, for example "1548843060657" and Perseo will parse this String with to a valid DateTime as 2019-01-30T10:11:00.657Z.

With None type attributes, Perseo will set the value to null in all cases.

None Attribute:

{
    "name": "nullAttribute",
    "type": "None",
    "value": "It does not matter what you put here"
}

This attribute will take null as value.

{
    "name": "nullAttribute2",
    "type": "None",
    "value": null
}

This attribute will take null as value.

Complete example using NGSv2 update action in a rule:

{
    "name": "blood_rule_update",
    "text": "select *,\"blood_rule_update\" as ruleName, *, ev.BloodPressure? as Pressure from pattern [every ev=iotEvent(BloodPressure? > 1.5 and type=\"BloodMeter\")]",
    "action": {
        "type": "update",
        "parameters": {
            "id": "${id}_example",
            "version": 2,
            "attributes": [
                {
                    "name": "pressure",
                    "type": "Number",
                    "value": "${Pressure}"
                }
            ]
        }
    }
}

Note that using NGSIv2 the BloodPressure attribute is a Number and therefore it is not necessary to use cast().

HTTP request action

Makes an HTTP request to an URL specified in url inside parameters, sending a body built from template. The parameters field can specify

  • method: optional, HTTP method to use, POST by default
  • URL: mandatory, URL target of the HTTP method
  • headers: optional, an object with fields and values for the HTTP header
  • qs: optional, an object with fields and values to build the query string of the URL
  • json: optional, an object that will be sent as JSON. String substitution will be performed in the keys and values of the object's fields. If present, it overrides template from action
 "action":{
      "type":"post",
      "template":"BloodPressure is ${BloodPressure}",
      "parameters":{
         "url": "http://localhost:9182/${type}/${id}",
         "method": "PUT",
         "headers": {
            "Content-type": "text/plain",
            "X-${type}-pressure": "${BloodPressure}"
         },
         "qs": {
            "${id}": "${BloodPressure}"
         }
      }
   }

Note that you can encode a JSON in the template field:

 "action": {
        "type": "post",
        "template": "{\"meter\":\"${Meter}\", \"pressure\": ${Pressure}}",
        "parameters": {
            "url": "http://${target_host}:${target_port}/myapp/${id}",
            "headers": {
                        "Content-type": "application/json",
                        "X-${type}-pressure": "${BloodPressure}"
            },
            "qs": {
                        "${id}": "${BloodPressure}"
            }
        }
    }

or use the json parameter

 "action": {
        "type": "post",
        "parameters": {
            "url": "http://${target_host}:${target_port}/myapp/${id}",
            "headers": {
                        "Content-type": "application/json",
                        "X-${type}-pressure": "${BloodPressure}"
            },
            "qs": {
                        "${id}": "${BloodPressure}"
            },
            "json": {
               "meter": "${meter}",
               "${id}": "${type}",
               "pressure": "${pressure}"
            }
        }
    }

The template and url fields and both the field names and the field values of qs and headers and json perform string substitution.

twitter action

Updates the status of a twitter account, with the text build from the template field. The field parameters must contain the values for the consumer key and secret and the access token key and access token secret of the pre-provisioned application associated to the twitter user.

 "action": {
        "type": "twitter",
        "template": "Meter ${Meter} has pressure ${Pressure} (GEN RULE)",
        "parameters": {
          "consumer_key": "xvz1evFS4wEEPTGEFPHBog",
          "consumer_secret": "L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg",
          "access_token_key": "xvz1evFS4wEEPTGEFPHBog",
          "access_token_secret": "L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg"
        }
    }

The template field performs string substitution.

Metadata and object values

Metadata values can be accessed by adding the suffix __metadata__x to the attribute name, being x the name of the metadata attribute. This name can be used in the EPL text of the rule and in the parameters of the action which accept string substitution. If the value of the metadata item is an object itself, nested fields can be referred by additional suffixes beginning with double underscore and the hierarchy can be walked down by adding more suffixes, like __metadata__x__subf1__subf12.

For example: The metadata in an event/notice like

{
  "subscriptionId" : "51c04a21d714fb3b37d7d5a7",
  "originator" : "localhost",
  "contextResponses" : [
    {
      "contextElement" : {
        "attributes" : [
          {
            "name" : "BloodPressure",
            "type" : "centigrade",
            "value" : "2",
            "metadatas": [{
              "crs": {
                "value": {"system": "WGS84"}
              }]
            }
          },
                {
            "name" : "TimeInstant",
            "type" : "urn:x-ogc:def:trs:IDAS:1.0:ISO8601",
            "value" : "2014-04-29T13:18:05Z"
          }
        ],
        "type" : "BloodMeter",
        "isPattern" : "false",
        "id" : "bloodm1"
      },
      "statusCode" : {
        "code" : "200",
        "reasonPhrase" : "OK"
      }
    }
  ]
}

could be used by a rule so

{
    "name": "blood_rule_email_md",
    "text": "select *, *,ev.BloodPressure? as Pression, ev.id? as Meter from pattern [every ev=iotEvent(cast(BloodPressure__metadata__crs__system?,String)=\"WGS84\" and type=\"BloodMeter\")]",
    "action": {
        "type": "email",
        "template": "Meter ${Meter} has pression ${Pression} (GEN RULE) and system is ${BloodPressure__metadata__crs__system}",
        "parameters": {
            "to": "someone@org.com",
            "from": "perseo_cep@telefonica.com",
            "subject": "MD VALUE: ${BloodPressure__metadata__crs__system}"
        }
    }
}

Generally, fields of attribute values which are objects themselves are accessible by adding to the name of the field a double underscore prefix, so an attribute x with fields a, b, c, will allow these fields to be referred as x__a, x__b and x__c.

Note: be aware of the difference between the key metadatas used in the context broker notificacions (v1), ending in s and the infix metadata, without the final s, used to access fields from EPL and actions.

Location fields

Fields with geolocation info with the formats recognized by NGSI v1, are parsed and generate two pairs of pseudo-attributes, the first pair is for the latitude and the longitude and the second pair is for the x and y UTMC coordinates for the point. These pseudo-attributes ease the use of the position in the EPL sentence of the rule. These derived attributes have the same name of the attribute with a suffix of __lat and __lon , and __x and __y respectively.

The formats are

So, a notification in the deprecated format like

{
    "subscriptionId": "57f73930e0e2c975a712b8fd",
    "originator": "localhost",
    "contextResponses": [
        {
            "contextElement": {
                "type": "Vehicle",
                "isPattern": "false",
                "id": "Car1",
                "attributes": [
                    {
                        "name": "position",
                        "type": "coords",
                        "value": "40.418889, -3.691944",
                        "metadatas": [
                            {
                                "name": "location",
                                "type": "string",
                                "value": "WGS84"
                            }
                        ]
                    }
                ]
            }
        }
    ]
}

will propagate to the core, (and so making available to the EPL sentence) the fields position__lat, position__lon , position__x, position__y

{
    "noticeId": "169b0920-8edb-11e6-838d-0b633312661c",
    "id": "Car1",
    "type": "Vehicle",
    "isPattern": "false",
    "subservice": "/",
    "service": "unknownt",
    "position": "40.418889, -3.691944",
    "position__type": "coords",
    "position__metadata__location": "WGS84",
    "position__metadata__location__type": "string",
    "position__lat": 40.418889,
    "position__lon": -3.691944,
    "position__x": 657577.4234800448,
    "position__y": 9591797.935076647
}

Analogously, a notification in "geopoint" format, like

{
    "subscriptionId": "57f73930e0e2c975a712b8fd",
    "originator": "localhost",
    "contextResponses": [
        {
            "contextElement": {
                "type": "Vehicle",
                "isPattern": "false",
                "id": "Car1",
                "attributes": [
                    {
                        "name": "position",
                        "type": "geo:point",
                        "value": "40.418889, -3.691944"
                    }
                ]
            },
            "statusCode": {
                "code": "200",
                "reasonPhrase": "OK"
            }
        }
    ]
}

will send to core an event with the fields position__lat, position__lon, position__x, position__y also

{
   "noticeId":"7b8f1c50-8eda-11e6-838d-0b633312661c",
   "id":"Car1",
   "type":"Vehicle",
   "isPattern":"false",
   "subservice":"/",
   "service":"unknownt",
   "position":"40.418889, -3.691944",
   "position__type":"geo:point",
   "position__lat":40.418889,
   "position__lon":-3.691944,
   "position__x":657577.4234800448,
   "position__y":9591797.935076647

An example of rule taking advantage of these derived attributes could be:

{
    "name": "rule_distance",
    "text": "select *, from pattern [every ev=iotEvent(Math.pow((cast(cast(position__x?,String),float) - 618618.8286057833), 2) + Math.pow((cast(cast(position__y?,String),float) - 9764160.736945232), 2) < Math.pow(5e3,2))]",
    "action": {
        "type": "email",
        "template": "${id} (${type}) is at ${position__lat}, ${position__lon} (${position__x}, ${position__y})",
        "parameters": {
            "to": "someone@tid.es",
            "from": "system@iot.tid.es",
            "subject": "${id} is coming"
        }
    }
}

that will send an email when the entity with attribute position is less than 5 km far away from Cuenca. It uses the circle equation, (x - a)^2 + (y - b)^2 = d^2, being (a, b) 618618.8286057833 and 9764160.736945232 the UTMC coordinates of Cuenca and d the distance of 5 000 m.

Note: for long distances the precision of the computations and the distortion of the projection can introduce some degree of inaccuracy.

Time fields

Some attributes and metadata, supposed to contain a time in ISO8601 format, will generate a pseudo-attribute with the same name as the attribute (or metadata field) and a suffix "__ts", with the parsed value as milliseconds for Unix epoch. This value makes easier to write the EPL text which involves time comparisons. The fields (attribute or metadata) supposed to convey time information are

  • Fields named TimeInstant
  • Fields of type DateTime
  • Fields of type urn:x-ogc:def:trs:IDAS:1.0:ISO8601

Additionally, some derived pseudo-attributes are included also

  • x__day: the day of the month (1-31) for the specified date according to local time.
  • x__month: the month (1-12) in the specified date according to local time.
  • x__year: the year (4 digits) of the specified date according to local time.
  • x__hour: the hour (0-23) in the specified date according to local time.
  • x__minute: the minutes (0-59) in the specified date according to local time.
  • x__second: the seconds (0-59) in the specified date according to local time.
  • x__millisecond: the milliseconds (0-999) in the specified date according to local time.
  • x__dayUTC: the day of the month (1-31) in the specified date according to universal time.
  • x__monthUTC: the month (1-12) in the specified date according to universal time.
  • x__yearUTC: the year (4 digits) of the specified date according to universal time.
  • x__hourUTC: the hour (0-23) in the specified date according to universal time.
  • x__minuteUTC: the minutes (0-59) in the specified date according to universal time.
  • x__secondUTC: the seconds (0-59) in the specified date according to universal time.
  • x__millisecondUTC: the milliseconds (0-999) in the specified date according to universal time.

So, an incoming notification like

{
    "subscriptionId": "51c04a21d714fb3b37d7d5a7",
    "originator": "localhost",
    "contextResponses": [
        {
            "contextElement": {
                "attributes": [
                    {
                        "name": "TimeInstant",
                        "value": "2014-04-29T13:18:05Z"
                    },
                    {
                        "name": "birthdate",
                        "type": "urn:x-ogc:def:trs:IDAS:1.0:ISO8601",
                        "value": "2014-04-29T13:18:05Z"
                    },
                    {
                        "name": "hire",
                        "type": "DateTime",
                        "value": "2016-10-13T12:10:44.149Z"
                    },
                    {
                        "name": "role",
                        "value": "benevolent dictator for life",
                        "metadatas": [
                            {
                                "name": "when",
                                "value": "2014-04-29T13:18:05Z",
                                "type": "DateTime"
                            }
                        ]
                    }
                ],
                "type": "employee",
                "isPattern": "false",
                "id": "John Doe"
            },
            "statusCode": {
                "code": "200",
                "reasonPhrase": "OK"
            }
        }
    ]
}

will send to core the "event"

{
    "noticeId": "799635b0-914f-11e6-836b-bf1691c99768",
    "noticeTS": 1476368120971,
    "id": "John Doe",
    "type": "employee",
    "isPattern": "false",
    "subservice": "/",
    "service": "unknownt",
    "TimeInstant": "2014-04-29T13:18:05Z",
    "TimeInstant__ts": 1398777485000,
    "TimeInstant__day": 29,
    "TimeInstant__month": 4,
    "TimeInstant__year": 2014,
    "TimeInstant__hour": 15,
    "TimeInstant__minute": 18,
    "TimeInstant__second": 5,
    "TimeInstant__millisecond": 0,
    "TimeInstant__dayUTC": 29,
    "TimeInstant__monthUTC": 4,
    "TimeInstant__yearUTC": 2014,
    "TimeInstant__hourUTC": 13,
    "TimeInstant__minuteUTC": 18,
    "TimeInstant__secondUTC": 5,
    "TimeInstant__millisecondUTC": 0,
    "birthdate": "2014-04-29T13:18:05Z",
    "birthdate__type": "urn:x-ogc:def:trs:IDAS:1.0:ISO8601",
    "birthdate__ts": 1398777485000,
    "birthdate__day": 29,
    "birthdate__month": 4,
    "birthdate__year": 2014,
    "birthdate__hour": 15,
    "birthdate__minute": 18,
    "birthdate__second": 5,
    "birthdate__millisecond": 0,
    "birthdate__dayUTC": 29,
    "birthdate__monthUTC": 4,
    "birthdate__yearUTC": 2014,
    "birthdate__hourUTC": 13,
    "birthdate__minuteUTC": 18,
    "birthdate__secondUTC": 5,
    "birthdate__millisecondUTC": 0,
    "hire": "2014-04-29T13:18:05Z",
    "hire__type": "DateTime",
    "hire__ts": 1398777485000,
    "hire__day": 29,
    "hire__month": 4,
    "hire__year": 2014,
    "hire__hour": 15,
    "hire__minute": 18,
    "hire__second": 5,
    "hire__millisecond": 0,
    "hire__dayUTC": 29,
    "hire__monthUTC": 4,
    "hire__yearUTC": 2014,
    "hire__hourUTC": 13,
    "hire__minuteUTC": 18,
    "hire__secondUTC": 5,
    "hire__millisecondUTC": 0,
    "role": "benevolent dictator for life",
    "role__metadata__when": "2014-04-29T13:18:05Z",
    "role__metadata__when__type": "DateTime",
    "role__metadata__when__ts": 1398777485000,
    "role__metadata__when__day": 29,
    "role__metadata__when__month": 4,
    "role__metadata__when__year": 2014,
    "role__metadata__when__hour": 15,
    "role__metadata__when__minute": 18,
    "role__metadata__when__second": 5,
    "role__metadata__when__millisecond": 0,
    "role__metadata__when__dayUTC": 29,
    "role__metadata__when__monthUTC": 4,
    "role__metadata__when__yearUTC": 2014,
    "role__metadata__when__hourUTC": 13,
    "role__metadata__when__minuteUTC": 18,
    "role__metadata__when__secondUTC": 5,
    "role__metadata__when__millisecondUTC": 0
}

A rule that will check if the employee has been hired in the last half hour, could be

{
    "name": "rule_time",
    "text": "select *, from pattern [every ev=iotEvent(cast(cast(hire__ts?,String),float) > current_timestamp - 30*60*1000)]",
    "action": {
        "type": "email",
        "template": "So glad with our new ${role}, ${id}!",
        "parameters": {
            "to": "someone@tid.es",
            "from": "system@iot.tid.es",
            "subject": "Welcome ${id}!"
        }
    }
}