Lua Scripting API

Exosite's One Platform can run Lua scripts on your behalf. These scripts have a rich set of capabilities and may be used to offload processing from your device.

If you're completely new to Exosite's APIs, you may want to read the API overview first.

Overview

Scripts are written in Lua 5.2 scripting language. For general information about Lua 5.2, please reference the online Lua manual.

Scripts may be added to an application either using Portals, or by using the Remote Procedure Call API. In Portals, scripts are added on the script management page.

To use the RPC to create a script, use the create RPC procedures. It's also possible to use the Exoline utility to upload a script from the command line using the script Exoline command. Run this command for details:

$ exo script --help

Scripts are associated with a client and may read, write, record and subscribe to data for any resources that client owns. For example, in Portals, if a script A is added to a particular device client it may access all of that device's datasources, but not the datasources of any other devices. If script B is added to a portal, it will have access to portal-level datasources as well as devices in that portal and their datasources.

A script may also dispatch messages using transports such as HTTP, SMS, Email, and XMPP.

Examples

Examples of One Platform Lua scripts are made available in this repository: Example Platform Scripts Repository

The script environment

Scripts are completely isolated from one another, each running in its own, secure environment, complete with access to a set of Lua tables and functions, as well as a set of One Platform API functions, the 'alias' table and Global properties and functions.

Scripts are scheduled to run with a limited number of execution ticks. When these ticks are consumed, the script is suspended until subsequently rescheduled.

Scripts also have memory usage limits, which if completely consumed terminates the script with a 'not enough memory' error. Please reference the Lua 5.2 manual for how Lua manages memory. In particular:

Lua tables and functions

The following global Lua tables and functions are available to Lua scripts. They operate exactly as described in the Lua 5.2 reference manual.

One Platform tables

The following One Platform resources and features are available to Lua scripts:

Global tables, functions, and properties

The following global resources and features are available to Lua scripts:

The alias table

Every script is owned by a One Platform client, and has access to that client and all of its resources through the alias table.

It is a good idea to create a local reference to specific aliases used in the script, e.g.:

local room_temp = alias['room_temp']

Each alias has properties and functions through which the script can interact with the aliased resource (for example to read from a dataport or write to a dataport)

  ------------------------------------------------------------------------------
  Values:      timestamp :: number | nil

  ------------------------------------------------------------------------------
  Status:           'ok' :: string - accessing .timestamp was successful
  ------------------------------------------------------------------------------
  Values:          value :: boolean | number | string | nil

  ------------------------------------------------------------------------------
  Status:           'ok' :: string - accessing .value was successful
  ------------------------------------------------------------------------------
  Values:           last :: number
  ------------------------------------------------------------------------------
  Arguments:             :: nil    - no expiration
                  expire :: number - expiration timestamp

  ------------------------------------------------------------------------------
  Returns:     timestamp :: number - timestamp of the next unprocessed datapoint
                         :: nil    - wait expired

  ------------------------------------------------------------------------------
  Status:           'ok' :: string - the call has returned without expiring
               'expired' :: string - the call has expired
  ------------------------------------------------------------------------------
  Values:  [<timestamp>] :: boolean | number | string | nil

  ------------------------------------------------------------------------------
  Status:           'ok' :: string - accessing [<timestamp>] was successful

The manage table

The manage table provides resource management functionality.

The manage table can be referenced globally and on client type alias objects.
When invoked globally, the functions in the table will act on behalf of the
script owner client. When invoked on an alias object, they will act on the
alias object owner's behalf.
  ------------------------------------------------------------------------------
  Arguments:        rid :: string - Client ID under which to activate the entity
                                    Note: an earlier form of this function
                                    allowed omitting this argument. That
                                    form is deprecated and should no
                                    longer be used.
                   type :: string - "client" | "share"
                   code :: string - Activation code

  ------------------------------------------------------------------------------
  Returns:          true :: boolean - Activation was successful
            false, error :: boolean, string - "badarg" | "invalid" | "noauth"
  ------------------------------------------------------------------------------
  Arguments:     rid :: string - Client ID under which to create a resource
                                 Note: an earlier form of this function
                                 allowed omitting this argument. That
                                 form is deprecated and should no
                                 longer be used.
                type :: string - "client" | "dataport" | "datarule" | "dispatch"
         description :: table - Description table specific to resource type.

  "client" description ::
    {
      limits = {
        client = <non_neg_integer> | "inherit",
        dataport = <non_neg_integer> | "inherit",
        datarule = <non_neg_integer> | "inherit",
        disk = <non_neg_integer> | "inherit",
        dispatch = <non_neg_integer> | "inherit",
        email = <non_neg_integer> | "inherit",
        email_bucket = <non_neg_integer> | "inherit",
        http = <non_neg_integer> | "inherit",
        http_bucket = <non_neg_integer> | "inherit",
        share = <non_neg_integer> | "inherit",
        sms = <non_neg_integer> | "inherit",
        sms_bucket = <non_neg_integer> | "inherit",
        xmpp = <non_neg_integer> | "inherit",
        xmpp_bucket = <non_neg_integer> | "inherit"
      },
      locked = true | false,
      meta = <string>,
      name = <string>,
      public = true | false,
    }

  "dataport" description ::
    {
      format = "binary" | "boolean" | "float" | "integer" | "string",
      meta = <string>,
      name = <string>,
      preprocess = {
        "add"|"sub"|"mod"|"div"|"mul"|"gt"|"lt"|"eq"|"geq"|"leq"|"neq"|"value" =
        <constant> ,...
      },
      public = true | false,
      retention = {
        count = <non_neg_integer> | "infinity",
        duration = <non_neg_integer> | "infinity"
      },
      subscribe = <rid> | nil
    }

  "datarule" description ::
    {
      format = "boolean" | "float" | "integer" | "string",
      meta = <string>,
      name = <string>,
      preprocess = {
        "add"|"sub"|"mod"|"div"|"mul"|"gt"|"lt"|"eq"|"geq"|"leq"|"neq"|"value" =
        <constant> ,...
      },
      public = true | false,
      retention = {
        count = <non_neg_integer> | "infinity",
        duration = <non_neg_integer> | "infinity"
      },
      rule = {
        simple = {
          comparision = "gt" | "lt" | "eq" | "geq" | "leq" | "neq",
          constant = <number>,
          repeat = true | false
        }
        |
        timeout = {
          repeat = true | false,
          timeout = <number>
        }
        |
        interval = {
          comparison = "gt" | "lt" | "eq" | "geq" | "leq" | "neq",
          constant = <number>,
          repeat = true | false,
          timeout = <number>
        }
        |
        duration = {
          comparison = "gt" | "lt" | "eq" | "geq" | "leq" | "neq",
          constant = <number>,
          repeat = true | false,
          timeout = <number>
        }
        |
        count = {
          comparison = "gt" | "lt" | "eq" | "geq" | "leq" | "neq",
          constant = <number>,
          count = <number>,
          repeat = true | false,
          timeout = <number>
        }
        |
        script = <string>
     }
     ,subscribe = <rid> | nil
    }

  "dispatch" description ::
    {
      locked = true | false,
      message = <string>,
      meta = <string>,
      method = "email" | "http_get" | "http_post" | "http_put" | "sms" | "xmpp",
      name = <string>,
      preprocess = {
        "add"|"sub"|"mod"|"div"|"mul"|"gt"|"lt"|"eq"|"geq"|"leq"|"neq"|"value" =
        <constant> ,...
      },
      public = true | false,
      retention = {
        count = <non_neg_integer> | "infinity",
        duration = <non_neg_integer> | "infinity"
      },
      subject = <string>,
      subscribe = <rid> | nil
    }

  ------------------------------------------------------------------------------
  Returns:    true, rid :: boolean, string - Resource created, id returned.
           false, error :: boolean, string - "badarg" | "invalid" | "limit" |
                                              "restricted"
  ------------------------------------------------------------------------------
  Arguments:        cid :: string - Client ID under which to deactivate the entity
                                    Note: an earlier form of this function
                                    allowed omitting this argument. That
                                    form is deprecated and should no
                                    longer be used.
                   type :: string - "client" | "share"
                   code :: string - Activation code
                   rid  :: string - Resource ID that code is associated with

  ------------------------------------------------------------------------------
  Returns:         true :: boolean - Deactivation was successful
           false, error :: boolean, string - "badarg" | "invalid" | "noauth"
  ------------------------------------------------------------------------------
  Arguments:        rid :: string - Resource ID to drop
                  alias :: string - Alias for resource ID to drop

  ------------------------------------------------------------------------------
  Returns:         true :: boolean - Drop was successful
           false, error :: boolean, string - "badarg" | "invalid" | "restricted"
  ------------------------------------------------------------------------------
  Arguments:        rid :: string - Resource ID to query
                  alias :: string - Alias for resource ID to query
                options :: table - Options specifying what information to return

    options - Specify one or more. Not all are valid for every resource type.
    {
      "aliases",
      "basic",
      "counts",
      "description",
      "key",
      "shares",
      "storage",
      "subscribers",
      "tagged",
      "tags",
      "usage"
    }

  ------------------------------------------------------------------------------
  Returns:   true ,info :: boolean ,table - Requested info successful returned
           false ,error :: boolean ,string - "badarg" | "invalid" | "restricted"

    info table structure:
    {
      -- for all resource types
      basic = {
        created = <timestamp>,
        modified = <timestamp>,
        subscribers = <number>,
        type = "client" | "dataport" | "datarule" | "dispatch"
      },
      -- see 'create' function for details about description
      description = <description>,
      shares = {
        {
          activator = <rid>,
          code = <code_string>,
          meta = <string>
        }
        ,...
      },
      subscribers = {
        {client | dataport | datarule | dispatch = {<rid>, ...}}, ...
      },
      tags = {
        <string>, ...
      },
      -- for clients:
      aliases = {
        {<rid>, {<string>, ...}}, ...
      },
      basic = {
        status = "activated" | "locked" | "notactivated" | "expired"
      },
      counts = {
        client = <number>,
        dataport = <number>,
        datarule = <number>,
        disk = <number>,
        dispatch = <number>,
        email = <number>,
        http = <number>,
        share = <number>,
        sms = <number>,
        xmpp = <number>
      },
      key = <cik_string>,
      usage = {
        client = <number>,
        dataport = <number>,
        datarule = <number>,
        disk = <number>,
        dispatch = <number>,
        email = <number>,
        http = <number>,
        share = <number>,
        sms = <number>,
        xmpp = <number>
      },
      tagged = {
        <string> ,...
      },

      -- for non-clients (dataport ,datarule ,dispatch)
      storage = {
        count = <number>,
        first = <timestamp>,
        last = <timestamp>,
        size = <number>
      },

      -- for datarules (scripts only):
      basic = {
        status = "completed" | "error" | "running" | "waiting",
        activity = {{<timestamp> = {<status>, ...}}, ...}
      },

      -- for dispatches:
      basic = {
        status = "normal" | "locked",
      }
    }
  ------------------------------------------------------------------------------
  Arguments:        rid :: string - Client ID under which to list resources
                                    Note: an earlier form of this function
                                    allowed omitting this argument. That
                                    form is deprecated and should no
                                    longer be used.
                  alias :: string - Alias for resource ID.
                   type :: table - List of resource types.
                options :: table - Categories of resources to return.

    type - Specify one or more.
    {
      "client",
      "dataport",
      "datarule",
      "dispatch"
    }

    options - Specify none or more.
    {
      "activated",
      "aliased",
      "owned",
      "public",
      "tagged"
    }

  ------------------------------------------------------------------------------
  Returns:   true ,list :: boolean ,table - Successful, list returned.
           false ,error :: boolean ,string - "badarg" | "invalid" | "restricted"

    list :: {{client | dataport | datarule | dispatch = {<rid> ,...}} ,...}
  ------------------------------------------------------------------------------
  Arguments:        cid :: string - Client ID under which to lookup a resource
                                    Note: an earlier form of this function
                                    allowed omitting this argument. That
                                    form is deprecated and should no
                                    longer be used.
                   type :: string - Lookup based on: owner, alias or code.
                    rid :: string - Resource ID (owner lookup only).
                  alias :: string - Alias for resource ID.
                   code :: string - Share activation code.

    type :: "aliased" | "owner" | "shared"

  ------------------------------------------------------------------------------
  Returns:    true ,rid :: boolean ,string - Successful, resource id returned.
           false ,error :: boolean ,string - "badarg" | "invalid" | "restricted"
  ------------------------------------------------------------------------------
  Arguments:       type :: string - Type of mapping: alias or tag.
                    rid :: string - Resource ID to alias or tag.
                  alias :: string - Alias for resource ID.
                mapping :: string - The alias or tag to map to resource ID.

    type :: "alias" | "tag"

  ------------------------------------------------------------------------------
  Returns:         true :: boolean         - Successful, resource id mapped.
           false, error :: boolean, string - "badarg" | "inuse" | "invalid" |
                                             "notfound" | "restricted"
  ------------------------------------------------------------------------------
  Arguments:       type :: string - Type of code: client or share
                   code :: string - Activation code

  ------------------------------------------------------------------------------
  Returns:         true :: boolean         - Successful, code revoked.
           false, error :: boolean, string - "badarg" | "invalid" | "noauth"
  ------------------------------------------------------------------------------
  Arguments:        rid :: string - Resource ID to be shared.
                  alias :: string - Alias for resource ID.
                options :: table  - Share options.

    options
    {
      meta = <string>
    }

  ------------------------------------------------------------------------------
  Returns:   true, code :: boolean, string - Successful, share code returned.
           false, error :: boolean, string - "badarg" | "invalid" | "limit" |
                                             "restricted"
  ------------------------------------------------------------------------------
  Arguments        rid  :: string - Resource ID to be tagged.
                action  :: string - can be 'add' or 'remove'.
                  name  :: string - The name of the tag

  ------------------------------------------------------------------------------
  Returns:         true :: boolean         - Successful, resource id tagged or
                                             tag removed.
           false, error :: boolean, string - "badarg"
  ------------------------------------------------------------------------------
  Arguments:        rid :: string - Client ID under which to unmap the mapping
                                    Note: an earlier form of this function
                                    allowed omitting this argument. That
                                    form is deprecated and should no
                                    longer be used.
                   type :: string - Type of mapping: alias only.
                mapping :: string - The mapped string to be removed.

    type :: "alias"

  ------------------------------------------------------------------------------
  Returns:         true :: boolean         - Successful, resource id mapped.
           false, error :: boolean, string - "badarg"
  ------------------------------------------------------------------------------
  Arguments:        rid :: string - Resource ID to be shared.
                  alias :: string - Alias for resource ID.
            description :: table - Description table specific to resource type.

  ------------------------------------------------------------------------------
  Returns:         true :: boolean         - Successful, resource updated.
           false, error :: boolean, string - "badarg" | "invalid" | "limit" |
                                             "restricted"
  ------------------------------------------------------------------------------
  Arguments:        rid :: string - Resource ID for which to query usage.
                  alias :: string - Alias for resource ID.
                 metric :: string - Usage metric to query.
              starttime :: number - Start of time window to query.
                endtime :: number - End of time window to query.

    metric :: "client" | "dataport" | "datarule" | "dispatch" | "share"
            | "email" | "http" | "sms" | "xmpp"

  ------------------------------------------------------------------------------
  Returns:  true, value :: boolean, number - Successful, usage value returned.
           false, error :: boolean, string - "badarg" | "invalid" | "restricted"

The JSON global table

Supports the encoding to and decoding from JSON format as specified at: http://www.json.org/

  ------------------------------------------------------------------------------
  Arguments:       table :: table  - the Lua table to be initialized as array

  ------------------------------------------------------------------------------
  Returns:         array :: table       - Lua table initialized as 'array'
          nil, error_msg :: nil ,string - error if table could not be
                                          initialized as a JSON-compliant array
  ------------------------------------------------------------------------------
  Arguments: json_string :: string - valid JSON encoded string

  ------------------------------------------------------------------------------
  Returns: decoded_value :: string | number | table | true | false | json.null
          nil, error_msg :: nil, string - error description encountered during
                                          decode
  ------------------------------------------------------------------------------
  Arguments:       value :: string | number | table | true | false | json.null

  ------------------------------------------------------------------------------
  Returns:  encoded_json :: string      - successfully encoded JSON string
          nil, error_msg :: nil, string - error description encountered during
                                          encode
  ------------------------------------------------------------------------------
  Arguments:       table :: table - the Lua table to be initialized as object

  ------------------------------------------------------------------------------
  Returns:        object :: table       - Lua table initialized as 'object'
          nil, error_msg :: nil, string - error if table could not be
                                          initialized as a JSON-compliant object
  ------------------------------------------------------------------------------
  Arguments:       value :: string | number | table | true | false | json.null
                            Lua value to check its type.

  ------------------------------------------------------------------------------
  Returns:          type :: string - "array" | "object" | "string" | "number" |
                                     "boolean" | "null"
                     nil :: nil    - the type of value is not a valid JSON type

The dispatch table

The One Platform supports sending messages to external recipients over several transports as 'dispatches' which are available via the 'dispatch' table.

The dispatch table can be referenced globally and on client type alias objects. When invoked globally, the functions in the table operate on behalf of the script owner client. When invoked through a client alias object, they will operate on behalf of that client.

Sends an email message to address with subject line. type is optional and specifies the email Content-Type header, and defaults to:

    "text/plain; charset=UTF-8"
  ------------------------------------------------------------------------------
  Arguments:    address :: string - valid email address
                subject :: string - email subject line text
                message :: string - text message
                   type :: string - (optional) message content type

  ------------------------------------------------------------------------------
  Returns:         true :: boolean         - email was successfully sent
          false, reason :: boolean, string - email was not successfully sent
  ------------------------------------------------------------------------------
  Arguments:    address :: string - valid email address
                subject :: string - headline subject line text
                message :: string - text message

  ------------------------------------------------------------------------------
  Returns:         true :: boolean         - headline was successfully sent
          false, reason :: boolean, string - headline was not successfully sent
  ------------------------------------------------------------------------------
  Arguments:        url :: string - fully formed url - eg "http://example.com/"
                 method :: string - request method, "post" | "put" | "get"
                   body :: string | nil - text message, if 'method' is "get",
                           'body' should be nil or it will be ignored. when
                           'method' is other than "get", 'body' must not be nil.
            contenttype :: string | nil - 'Content-Type' header value, if
                           'method' is "get", 'contenttype' should be nil or it
                           will be ignored. when 'method' is other than "get",
                           'contenttype' must not be nil.
                headers :: table | nil  - custom header table or nil
                           e.g. {'custom_1'="value_1",'custom_2'='value_2'}
                timeout :: number | nil - number of miliseconds before
                           the request times out or nil.

  ------------------------------------------------------------------------------
  Returns: true, result :: boolean, table  - request was successfully made
          false, reason :: boolean, string - request was not successfully made

    result
    {
      status = <number>,
      headers = {<header> = <string> ,...},
      body = <string>
    }
  ------------------------------------------------------------------------------
  Arguments:     number :: string - phone number, including country code
                message :: string - text message

  ------------------------------------------------------------------------------
  Returns:         true :: boolean         - sms was successfully sent
          false, reason :: boolean, string - sms was not successfully sent

Note: Currently true is always returned. This function does not wait for delivery becuase SMS can take several minutes or more to be delivered. There is currently no way to verify that a message has been delivered.

  ------------------------------------------------------------------------------
  Arguments:       auth :: table  - authentication table (see below)
                message :: string - text message

  The 'auth' table has four fields:

     oauth_consumer_key :: string - Consumer Key
  oauth_consumer_secret :: string - Consumer Secret
            oauth_token :: string - Token
     oauth_token_secret :: string - Token Secret

  Authentication with www.twitter.com requires that a user have a twitter.com
  account and, under their user account, a twitter application, on behalf of
  which, the One Platform script will be able to send tweets. Follow the process
  described on www.twitter.com to set up an account and an application for your
  account. Then, under the new application's settings, obtain the above four
  values.

  ------------------------------------------------------------------------------
  Returns:         true :: boolean         - tweet was successfully sent
          false, reason :: boolean, string - tweet was not successfully sent

  twitter.com rejects subsequent, identical tweets. This can result in failure
  reason "undelivered".

Utilities

The scripting system has access to utilities functions in the global scope.

  ------------------------------------------------------------------------------
  Arguments:     format :: string - '*t' or strftime-compliant format specifier
                                    See http://linux.die.net/man/3/strftime
                                    If omitted, defaults to '%c'.
                   time :: number - UNIX time value to be formatted
                                    If omitted, defaults to current UTC time.

  ------------------------------------------------------------------------------
  Returns:         date :: string - formatted date and/or time string
                   date :: table  - if format was specified as '*t', the Lua
                                    table with the following number fields:
                                     .sec   - seconds              (0-59)
                                     .min   - minutes              (0-59)
                                     .hour  - hours                (0-23)
                                     .day   - day of month         (1-31)
                                     .month - month                (1-12)
                                     .year  - year
                                     .wday  - weekday              (Sun=1-7)
                                     .yday  - days since January 1 (1-366)

    nil, "badspecifier" :: nil, string - if invalid format specifier was given
  ------------------------------------------------------------------------------
  Arguments:     essage :: string - debug message

  ------------------------------------------------------------------------------
  Returns:         nil

Locale controls formatting and internationalization of various outputs. In the One Platform scripting environment, these are the date function and some of the built in Lua functions, such as tonumber.

By default, scripts use the en_US.utf8 locale. Scripts can switch to another locale by calling this function and specifying the new locale. The new locale remains in effect until a subsequent invocation of this function.

  ------------------------------------------------------------------------------
  Arguments:     locale :: string - the name of the locale

    Supported are the 'C' and 'POSIX' locales and the <language>_<country>.utf8
    locales as follows:

    aa_DJ, af_ZA, an_ES, ar_AE, ar_BH, ar_DZ, ar_EG, ar_IQ, ar_JO, ar_KW, ar_LB,
    ar_LY, ar_MA, ar_OM, ar_QA, ar_SA, ar_SD, ar_SY, ar_TN, ar_YE, as_IN, az_AZ,
    be_BY, bg_BG, br_FR, bs_BA, ca_AD, ca_ES, ca_FR, ca_IT, cs_CZ, cy_GB, da_DK,
    de_AT, de_BE, de_CH, de_DE, de_LI, de_LU, el_CY, el_GR, en_AU, en_BW, en_CA,
    en_DK, en_GB, en_HK, en_IE, en_NZ, en_PH, en_SG, en_US, en_ZA, en_ZW, es_AR,
    es_BO, es_CL, es_CO, es_CR, es_DO, es_EC, es_ES, es_GT, es_HN, es_MX, es_NI,
    es_PA, es_PE, es_PR, es_PY, es_SV, es_US, es_UY, es_VE, et_EE, eu_ES, eu_FR,
    fi_FI, fo_FO, fr_BE, fr_CA, fr_CH, fr_FR, fr_LU, ga_IE, gd_GB, gl_ES, gv_GB,
    he_IL, hr_HR, hu_HU, id_ID, is_IS, it_CH, it_IT, iw_IL, ja_JP, ka_GE, kk_KZ,
    kl_GL, ko_KR, ku_TR, kw_GB, lg_UG, lt_LT, lv_LV, mg_MG, mi_NZ, mk_MK, ms_MY,
    mt_MT, nb_NO, nl_BE, nl_NL, nn_NO, oc_FR, om_KE, pl_PL, pt_BR, pt_PT, ro_RO,
    ru_RU, ru_UA, sk_SK, sl_SI, so_DJ, so_KE, so_SO, sq_AL, st_ZA, sv_FI, sv_SE,
    tg_TJ, th_TH, tl_PH, tr_CY, tr_TR, tt_RU, uk_UA, uz_UZ, wa_BE, xh_ZA, yi_US,
    zh_CN, zh_HK, zh_SG, zh_TW, zu_ZA

  ------------------------------------------------------------------------------
  Returns:         true :: boolean - new locale was successfully set
                  false :: boolean - locale was not successfully set
  ------------------------------------------------------------------------------
  Arguments: timezone :: string - the name of timezone

    For a list of available time zones, reference 'List' table in the following
    article: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones

    For the argument to this function, use the timezone name values found in the
    TZ* column of that table.

  ------------------------------------------------------------------------------
  Returns:         true :: boolean - the new timezone was successfully set
                  false :: boolean - the new timezone was not successfully set