Skip to content

The qToggle API 1.1

Calin Crisan edited this page Mar 12, 2023 · 8 revisions

Abstract

The qToggle API is an intuitive HTTP API that enables remote controlling of basic hardware ports such as GPIOs or ADCs.

qToggle provides a simple language for the Internet of Things by employing the common JSON data format. Turning on a light bulb should be as easy as PATCH-ing a URL, while reading the temperature from a sensor requires no more than a GET request.

The idea behind qToggle is to control programmable systems having a TCP/IP stack via simple HTTP requests. These systems can be, for example, single-board computers or TCP/IP-enabled microcontrollers.

API functions are grouped into the following categories:

  • device management - general status and configuration of the device
  • port management - port information and configuration
  • port values - reading and writing values from and to ports
  • notifications - event notifications
  • reverse API calls - API calls via reverse HTTP requests

Some of the functionalities (along with their corresponding function calls) are optional.

Versions

The API version is composed of two numbers: M.m; M is the major version, while m is the minor version.

The minor version is increased when new API functionality that is backwards compatible is introduced. An increase in the major version indicates changes that are not backwards compatible.

Definitions & Terminology

Devices

A qToggle-enabled device is any machine that implements the mandatory API functions described by this document.

Consumers

A qToggle consumer is any machine that consumes the API (calls the API functions) described by this document, or is notified of events generated by qToggle devices.

Ports

qToggle deals with ports. Port values can be written to or read from ports. The precise meaning of a port and the actual effect of reading or writing values from or to ports are left to the implementation. A common example of a port is a GPIO.

Each port has a type which indicates the data type of values that are read from or written to the port. All possible types ports can have are defined in Port Types.

The device can detect the presence or the absence of a port at runtime and decide to add or remove it, respectively.

Port Types

Each port in a qToggle device has one of the following types:

  • boolean, usually representing digital pins (GPIOs). Its value has a boolean data type.
  • number, usually representing analog ports, such as ADCs or DACs. Its value has a number data type.

Port Writing Process

The process of writing a value to a port is rarely instantaneous; operations required to complete a port write are usually not atomic and may take a non-negligible amount of time. Asynchronous device implementations should yield during these write operations, allowing other concurrent processes to carry on.

Devices should maintain a queue of pending port values that were pushed for writing, for each port. At least one pending value must be kept for each port, the one that's currently in the process of being written, but for which the process has not yet completed. This value will simply be referred to as the port's pending value.

Port Reading Process

All enabled ports should be read in a loop, continuously, with a sampling rate that may be configured on a port basis or decided by the implementation. A port's current value is always the most recent successfully read value. If a port's value could not be read at all since the port has been enabled, its current value will be unavailable.

Port Value Changes

The device should always keep the last value for each port, known as current value. The current value of a port is determined by continuous reads, as described in Port Reading Process. Whenever the value of an enabled port changes compared to the current value, the current value should be updated accordingly and any mechanisms that depend on the change of that port should be triggered.

The change can have one of the following causes:

The device should be prepared to handle port values that are unavailable (cases when no value is known for a certain port).

Virtual Ports

Virtual ports are an optional functionality. A virtual port is a port with no hardware representation, used for its ability to store a value and to be part of an expression. All mechanisms should treat virtual ports just as any other regular port; reading from and writing to them, however, should be immediate and should have no additional side effect.

Virtual ports can be added or removed. A device supporting virtual ports must expose the attribute virtual_ports having an integer value equal to the maximum number of supported virtual ports. All virtual ports must expose the attribute virtual with value true. Virtual ports may expose additional attributes, just like any other regular port.

Virtual ports are always writable.

Special API functions are supported by the device for adding and removing virtual ports. These functions are:

Attributes

Devices and ports expose attributes that give information about the device/port or provide means to configure it. Some of the attributes are modifiable. Each attribute has a value of a certain data type and may impose restrictions on accepted values (such as a minimum and/or a maximum, or a limited set of values). Possible data types for attributes are boolean, number and string.

Attributes, whether they belong to ports or devices, may be either standard or additional. Standard attributes are well defined by this document (see Standard Device Attributes and Standard Port Attributes), while additional attributes are implementation-specific and are out of the scope of this document.

While the definitions of standard attributes are implicit (as defined by this document), a definition should be provided for each of the additional attributes. Attributes with no implicit or explicit definition should be ignored by the consumer.

Devices may override (parts of the) the definition of a standard attribute by explicitly supplying a definition for it.

Each device must expose a set of standard Common Device Attributes, just as well as all ports must expose a set of standard Common Port Attributes.

Devices may expose standard Optional Device Attributes and ports may expose standard Optional Port Attributes. Optional attributes, if exposed, must have the definition and the meaning imposed by this document. Some of the optional attributes must be exposed by certain ports in certain circumstances, as noted by their description.

The list of attributes exposed by a port or a device is not fixed - it may change during the functioning of the device, meaning that new attributes may appear and existing attributes may be removed. The same applies to their definitions.

An attribute has the following fields:

  • name field of type string is the name of the attribute; field name restrictions apply to this field; in addition to that, attribute names may not contain dots

  • display_name is an optional string field that can be used by the consumer to show the attribute in a user interface. The maximum length of this field's value is 64.

  • description field of type string is a human-readable description of the attribute. The maximum length of this field's value is 64.

  • type field of type string indicates the data type of the attribute and is one of: "boolean", "number", "string".

  • modifiable field of type boolean tells whether the attribute can be modified or not.

  • unit is an optional string unit of measurement for the attribute (e.g. "seconds"). The maximum length of this field's value is 16.

  • min and max are optional number fields used by attributes of type number and string to represent the range of accepted values, or the minimum and maximum length of the string, respectively.

  • integer is an optional boolean field specific to attributes of type number and, if present, tells whether the attribute value must be an integer or it can be any real number.

  • step is an optional number field specific to attributes of type number and, if present, restricts valid values to those equal to min plus a multiple of step; this field only makes sense when min is defined.

  • choices is an optional list of dictionaries, valid only for attributes of types number or string and, if present, restricts the accepted values to a limited set of choices. A choice dictionary has the following fields:

    • value is a mandatory field having the same type as the attribute's type (number or string) and represents the choice value.
    • display_name is an optional field of type string that can be used by consumers to display the choice in a user interface.
  • reconnect is an optional boolean field, valid only for modifiable attributes, indicating that the consumer should expect the device to disconnect and reconnect shortly after updating this attribute.

Reserved Identifiers

The following identifiers are reserved and must not be used as attribute names, device names or port identifiers:

  • "add"
  • "definitions"
  • "delete"
  • "remove"
  • "reverse"
  • "scan"
  • "value"
  • "pending_value"
  • "webhooks"

Standard Device Attributes

This section defines all the standard device attributes. For more details see Attributes.

Common Device Attributes

These are attributes that must be exposed by all devices.

name

This attribute is a name given to the device (usually the hostname). The general field name restrictions apply to this attribute. The device name however may not contain dots.

This string attribute is modifiable. The maximum length of this attribute's value is 32 and it cannot be empty.

display_name

This attribute is a human-readable text that can be used by the consumer to show the device in a user interface.

This string attribute is modifiable. The maximum length of this attribute's value is 64.

version

This attribute is a string representing the firmware and/or hardware version of the device.

This string attribute is not modifiable.

api_version

This attribute represents the version of the implemented qToggle API.

This string attribute is not modifiable.

vendor

This attribute indicates vendor of this qToggle API implementation. The recommended format is "{organization}/{implementation_name}".

This string attribute is not modifiable.

admin_password

This attribute represents the device administrator password, associated to the admin username. See Authentication for more details.

This attribute should never be disclosed by the device; when listing this attribute's value, devices should instead return the string "set" if the password is not empty or an empty string otherwise.

This string attribute is modifiable. The maximum length of this attribute's value is 32.

flags

This attribute contains the device flags, which indicate the availability of various optional functionalities.

The following flags are defined:

  • "backup" indicates whether the optional Backup & Restore mechanism is supported or not.
  • "expressions" indicates whether the optional Expressions are supported or not.
  • "firmware" indicates whether the optional firmware update is supported or not.
  • "history" indicates whether the optional History mechanism is supported or not.
  • "listen" indicates whether the optional Listening mechanism is supported or not.
  • "reverse" indicates whether the optional The Reverse API Calls Mechanism is supported or not.
  • "sequences" indicates whether the optional value sequences are supported or not.
  • "tls" indicates whether the optional HTTPS scheme is supported or not.
  • "webhooks" indicates whether the optional Webhooks are supported or not.

Aside from these defined flags, the device may expose other flags.

This attribute is a list of string and is not modifiable.

Optional Device Attributes

normal_password

This attribute represents the password associated to the optional normal username. See Authentication for more details.

This attribute should never be disclosed by the device; when listing this attribute's value, devices should instead return the string "set" if the password is not empty or an empty string otherwise.

This string attribute is modifiable. The maximum length of this attribute's value is 32.

viewonly_password

This attribute represents the password associated to the optional viewonly username. See Authentication for more details.

This attribute should never be disclosed by the device; when listing this attribute's value, devices should instead return the string "set" if the password is not empty or an empty string otherwise.

This string attribute is modifiable. The maximum length of this attribute's value is 32.

uptime

This attribute represents the number of seconds elapsed since the device has been turned on. The device should not trigger a device-update event whenever this attribute changes, to avoid generating such an event each second.

This number attribute is not modifiable.

date

This attribute should be exposed by devices that support real date/time to allow reading (and optionally setting) the system date. The device should not trigger a device-update event whenever this attribute changes, to avoid generating such an event each second.

The system date is expressed as seconds since Epoch.

This number attribute is modifiable.

timezone

This attribute should be exposed by devices that support real date/time to allow reading and setting the local time zone.

The local time zone is expressed as a IANA tzdata string in the following format: Area/Location.

The client should consider a list of choices for this attribute that is up to date with timezones published by IANA: https://www.iana.org/time-zones.

This string attribute is modifiable.

ip_address

This attribute could be exposed by devices that allow manual IP configuration and represents the manual device IP address. An empty string value indicates use of automatic IP configuration (DHCP). The format used is X.X.X.X.

Upon changing this attribute, the device should reestablish the network connection using the new configuration. The consumer may assume that the reconnect field of this attribute is always true.

This string attribute is modifiable.

ip_address_current

This attribute represents the current device IP address. The format used is X.X.X.X.

If there's no current IP connection, the value of this attribute is an empty string.

This string attribute is not modifiable.

ip_netmask

This attribute could be exposed by devices that allow manual IP configuration and represents the network mask. The value is a number between 0 and 31, representing the mask length. Value 0 indicates use of automatic IP configuration (DHCP).

Upon changing this attribute, the device should reestablish the network connection using the new configuration. The consumer may assume that the reconnect field of this attribute is always true.

This number attribute is modifiable.

ip_netmask_current

This attribute represents the current network mask length. It's a number between 0 and 31.

If there's no current IP connection, the value of this attribute is an empty string.

This number attribute is not modifiable.

ip_gateway

This attribute could be exposed by devices that allow manual IP configuration and represents the network gateway (default route). An empty string value indicates use of automatic IP configuration (DHCP). The format used is X.X.X.X.

Upon changing this attribute, the device should reestablish the network connection using the new configuration. The consumer may assume that the reconnect field of this attribute is always true.

This string attribute is modifiable.

ip_gateway_current

This attribute represents the current network gateway (default route). The format used is X.X.X.X.

If there's no current IP connection, the value of this attribute is an empty string.

This string attribute is not modifiable.

ip_dns

This attribute could be exposed by devices that allow manual IP configuration and represents the DNS server. An empty string value indicates use of automatic IP configuration (DHCP). The format used is X.X.X.X.

Upon changing this attribute, the device should reestablish the network connection using the new configuration. The consumer may assume that the reconnect field of this attribute is always true.

This string attribute is modifiable.

ip_dns_current

This attribute represents the current DNS server. The format used is X.X.X.X.

If there's no current IP connection, the value of this attribute is an empty string.

This string attribute is not modifiable.

wifi_ssid

This attribute could be exposed by Wi-Fi-enabled devices and represents the Wi-Fi network name. An empty string value disables the Wi-Fi connection.

This string attribute is modifiable. The maximum length of this attribute's value is 32.

Upon changing this attribute, the device should connect to the new network. The consumer may assume that the reconnect field of this attribute is always true.

wifi_key

This attribute could be exposed by Wi-Fi-enabled devices and represents the Wi-Fi network key (password). An empty string value indicates an open Wi-Fi network.

This string attribute is modifiable. The maximum length of this attribute's value is 64.

Upon changing this attribute, the device should reconnect to the network using the new key. The consumer may assume that the reconnect field of this attribute is always true.

wifi_bssid

This attribute could be exposed by Wi-Fi-enabled devices and represents a specific Wi-Fi BSSID. When a specific BSSID is set, the device must connect to the access point with that BSSID. The format of this attribute's value is XX:XX:XX:XX:XX:XX.

This string attribute is modifiable. The maximum length of this attribute's value is 32.

Upon changing this attribute, the device should connect to the access point having the specified BSSID. The consumer may assume that the reconnect field of this attribute is always true.

wifi_bssid_current

This attribute could be exposed by Wi-Fi-enabled devices and represents the BSSID of the access point to which the device is currently connected. The format used is XX:XX:XX:XX:XX:XX.

If there's no current Wi-Fi connection, the value of this attribute is an empty string.

This string attribute is not modifiable.

wifi_signal_strength

This attribute could be exposed by Wi-Fi-enabled devices and represents an indication of the current Wi-Fi signal strength.

Values range from 0 (weak) to 3 (excellent). Value -1 could be used if there's no Wi-Fi connection.

This number attribute is not modifiable.

temperature

This attribute could be exposed by devices to indicate their (core) temperature, in degrees celsius.

This number attribute is not modifiable.

cpu_usage

This attribute could be exposed by devices to indicate their total, average CPU usage.

The device should avoid triggering a device-update event whenever this attribute changes, to prevent generating a large number of events. Instead, the consumer should use polling to sample it as often as required.

The usage is expressed as percent, from 0 to 100.

This number attribute is not modifiable.

mem_usage

This attribute could be exposed by devices to indicate the percentage of RAM used.

The usage is expressed as percent, from 0 to 100.

This number attribute is not modifiable.

storage_usage

This attribute could be exposed by devices to indicate the used percent of their storage flash or disk.

The usage is expressed as percent, from 0 to 100.

This number attribute is not modifiable.

battery_level

This attribute could be exposed by battery-powered devices to indicate the battery state of charge.

The charge level is expressed as percent, from 0 to 100.

This number attribute is not modifiable.

low_battery

This attribute could be exposed by battery-powered devices to indicate that the battery state of charge is low.

This boolean attribute is not modifiable.

virtual_ports

This attribute indicates the maximum number of the optional Virtual Ports that are supported by the device.

If the device does not support virtual ports, the attribute must not be exposed.

This number attribute is not modifiable.

config_name

This attribute indicates a particular configuration of the device (e.g. a certain type of thermostat, smart switch, etc). The attribute may be used by consumers to provision the device with a default configuration as well as by devices themselves for autoconfiguration and self-provisioning.

This string attribute is modifiable.

firmware_auto_update

When this attribute is true, the device will automatically handle firmware updates. The device should check for new firmware releases at regular intervals, decided by the implementation.

This boolean attribute is modifiable.

Standard Port Attributes

This section defines all the standard port attributes. For more details see Attributes.

Common Port Attributes

These are attributes that must be exposed by all ports.

id

This is an identifier that is unique per qToggle device and represents in a way the name of the port.

The general field name restrictions apply to this attribute.

This string attribute is not modifiable. The maximum length of this attribute's value is 64.

display_name

This is a human-readable text that can be used by consumers to show the port in a user interface.

This string attribute is modifiable. The maximum length of this attribute's value is 64.

type

This attribute is the type of the port, as described in Port Types.

This string attribute is not modifiable.

writable

This attribute tells whether the port can be written to or not.

A port can always be read from; reading the value of an output-only port could simply provide the last written value, for example.

This boolean attribute is not modifiable.

enabled

This attribute tells whether the port is currently enabled or not.

A disabled port cannot be read from or written to.

This boolean attribute is modifiable.

Optional Port Attributes

unit

This attribute represents the unit of measurement of the port (e.g. "degrees"). Units can be displayed next to port values in user interfaces.

It must be exposed by all ports of type number.

This string attribute is modifiable. The maximum length of this attribute's value is 16.

tag

This attribute may be exposed by any port.

It may be used by consumers to group ports.

This string attribute is modifiable. The maximum length of this attribute's value is 64.

choices

This attribute attribute may be exposed by number ports to limit the accepted values. The device should be able to handle at least 32 choices for a port.

This attribute is a list of dictionaries with the following fields:

  • value is a mandatory field of type number and represents the choice value.
  • display_name is an optional field of type string that can be used by consumers to display the choice in a user interface.

This attribute is not modifiable.

min

This attribute may be exposed by number ports to indicate a minimum accepted value.

This number attribute is not modifiable.

max

This attribute may be exposed by number ports to indicate a maximum accepted value.

This number attribute is not modifiable.

integer

This attribute may be exposed by number ports to restrict the accepted value to an integer (as opposed to a real number).

In case of ports that have the transform_read attribute set, the integer constraint refers to the value before applying the transformation (i.e. the one read directly from the port). In case of writable ports that have the transform_write attribute set, the integer constraint refers to the value after applying the transformation (i.e. the one written directly to the port). In this latter case, devices should round the resulting value before writing it to the port. Moreover, the consumer should prevent integer validation when dealing with values read from/written to ports with read/write transform attributes.

This boolean attribute is not modifiable.

step

This attribute may be exposed by number ports to restrict accepted values to those equal to min plus a multiple of step; this attribute only makes sense when min is defined.

This number attribute is not modifiable.

virtual

This attribute must be exposed by Virtual Ports and its value must be true.

This boolean attribute is not modifiable.

persisted

This attribute may be exposed by any port to indicate whether the port value should be preserved (persisted) across power losses or system resets.

This boolean attribute is modifiable.

online

This attribute may be exposed by any port to indicate if the port is currently online (available, connected) or not.

A disabled port cannot be online.

This boolean attribute is not modifiable.

internal

This attribute could be exposed by a port to indicate that the port's usage is limited to the device scope and its value has no meaning outside of the device. It may be used by consumers to ignore values from this port or to simply ignore the port entirely.

This boolean attribute is modifiable.

expression

This attribute represents the expression configured for this port.

An empty value means no expression configured for the port. Only writable ports should expose this attribute. If device advertises the "expressions" flag, then all writable ports must expose this attribute.

This string attribute is modifiable. The maximum length of this attribute's value is 1024.

transform_write

This attribute represents an expression used to transform the value right before writing it to the port.

An empty value means no writing transformation. This attribute should only be exposed by ports with writable set to true.

This string attribute is modifiable. The maximum length of this attribute's value is 1024.

transform_read

This attribute represents an expression used to transform the value right after being read from the port.

An empty value means no reading transformation.

This string attribute is modifiable. The maximum length of this attribute's value is 1024.

history_interval

This attribute indicates how often to save a sample of the port value to history, in seconds.

If device advertises the "history" flag, then all ports must expose this attribute.

Special value 0 disables history saving, while -1 will determine saving of a port value sample each time the value changes.

This number attribute is modifiable. Valid values range from -1 to 2147483647.

history_retention

This attribute indicates for how long the device will keep history data for this port, in seconds.

If device advertises the "history" flag, then all ports must expose this attribute.

Special value 0 indicates indefinite retention. The device may however remove old history data if running out of storage space.

This number attribute is modifiable. Valid values range from 0 to 2147483647.

Expressions

Instead of supplying explicit values to a writable port, it can be configured to use a value expression. When using expressions, a port's value will be calculated as a result of a formula that can include other port values, literal values and functions. Expressions are an optional functionality that may or may not be supported by a port. Controlling the value expression of a port is done via the expression attribute. The presence of the expression port attribute indicates support for this feature. Setting an empty expression has the effect of disabling value expressions for that port.

In addition to expressions used to compute the port value, called value expressions, the value can be altered before being actually written to the port, using an expression configured in the transform_write attribute. It can also be altered right after being read from the port, using the expression configured in the transform_read attribute. Using these two attributes called transform expressions, one can, for example, invert the logic value of a port, or apply a linear transformation to a numeric port. Ideally, the two transform expressions would be the inverse of each other. However this is not a constraint, as the user could configure two completely unrelated transform expressions for a port.

If a port has both expression and transform_write attributes set, the value that is actually written to it must first be generated by expression and then passed through transform_write, just like any other value written to the port.

A port cannot have an expression and a sequence at the same time. If the port is executing a sequence when a request to change the expression attribute is received, the sequence must be cancelled. Using PATCH /ports/{id}/value on a port configured to use an expression should have its normal behavior; the value will however be overwritten by the next expression evaluation. A call to PATCH /ports/{id}/sequence on an expression-enabled port must be rejected. Transform expressions do not interfere with any of these API calls.

Expressions depending on inexistent or disabled ports must simply be ignored; these expressions are said to have an unavailable value. They should not be removed from the port, nor should they affect the value of the port in any way.

Some expressions require an internal state where they store details such as recent last few values or timing information. This state should not be persisted across device resets. The state of port expressions must be reset when expression is changed or upon port disabling/enabling.

Syntax

All three attributes use the same expression syntax. The maximum length of an expression is imposed by the maximum length of a string value, which is 1024 characters. Expressions have the following simplified formal definition:

expression = literal | $ | $port_id | @port_id | function(expression, ...)

In other words, an expression may be one of the following kinds:

  • literal values
  • port values represented by the port identifier preceded by a $ sign; if the port identifier is omitted, the expression refers to the port to which it is assigned, being refered to as a self port expression
  • port references represented by the port identifier preceded by an @ sign; if the port identifier is omitted, the expression refers to the port to which it is assigned
  • functions whose arguments are themselves expressions

Expressions are case-sensitive. Any number of white space characters can be present anywhere around literal values, function names or port names.

A function may have zero, one or more comma-separated arguments; regardless of the number of arguments, the function name must be followed by a pair of parentheses. Some functions may accept a variable number of arguments.

Functions may require that some of their arguments be represented by an expression of a particular kind, as indicated in their description. By default, functions accept any kind of arguments, except for port references, unless otherwise indicated.

Expression Validation

Additional checks should be performed by the device to detect circular dependencies when a value expression is set. A port's value expression can however depend directly on the port itself without creating a circular dependency; in this case, the new value will in fact depend on the current value of that port. For example, the following value expression, when associated to port gpio0, must be evaluated when gpio1 or gpio2 change but not when gpio0 changes:

IF(OR($gpio1, $gpio2), NOT($), $)

The following expressions are considered invalid and must not be associated to a port:

  • expressions that are not syntactically correct
  • expressions containing functions whose arguments do not respect the required expression kind
  • expressions that create circular dependencies

A request to change any of the expression attributes to an invalid value must be responded with a 400 Bad Request, with the error field set to "invalid-field" and field field set to "expression". An additional field called details must contain the detailed reason why expression was found invalid. The following reasons are defined:

Unknown Function

This reason should be used when a given function is not recognized. Field details has the following format:

"details": {
    "reason": "unknown-function",
    "token": "<function-name>",
    "pos": <position>
}

The position is the start position of the unknown function name. First character in expression has position 1.

Invalid Number Of Arguments

This reason should be used when a given function is called with the wrong number of arguments. Field details has the following format:

"details": {
    "reason": "invalid-number-of-arguments",
    "token": "<function-name>",
    "pos": <position>
}

The position is the start position of the function name. First character in expression has position 1.

Invalid Argument Kind

This reason should be used when a given function is called with an invalid argument kind. Field details has the following format:

"details": {
    "reason": "invalid-argument-kind",
    "token": "<function-name>",
    "pos": <position>,
    "num": <argument-number>
}

The position is the start position of the argument name. First character in expression has position 1. num indicates the invalid argument number, starting at 1.

Unbalanced Parentheses

This reason should be used when a closing parenthesis that has no opening pair is encountered. Field details has the following format:

"details": {
    "reason": "unbalanced-parentheses",
    "pos": <position>
}

The position indicates the place where the unbalanced parenthesis was found. First character in expression has position 1.

Unexpected End

This reason should be used when expression parsing ended but not all parentheses were closed. Field details has the following format:

"details": {
    "reason": "unexpected-end"
}

Circular Dependency

This reason should be used when a loop is detected in the dependency graph of the expressions. Field details has the following format:

"details": {
    "reason": "circular-dependency"
}

Unexpected Character

This reason should be used when an unexpected character is encountered in the expression. Field details has the following format:

"details": {
    "reason": "unexpected-character",
    "token": "<character>",
    "pos": <position>
}

The position indicates the place where the unexpected character was found. First character in expression has position 1.

Empty

This reason should be used when the given expression is empty (or contains only whitespace). Field details has the following format:

"details": {
    "reason": "empty"
}

Too Long

This reason should be used when the given expression length exceeds maximum supported length. Field details has the following format:

"details": {
    "reason": "too-long"
}

Expression Evaluation

Expressions are evaluated upon specific triggers and the resulted value is then used to decide a port's read or written value.

Evaluation Triggers

A value expression that belongs to an enabled port must be evaluated each time one of the port values from the expression change, as described in Port Value Changes. Port value expressions must also be evaluated immediately after being assigned to a port and when the respective port becomes enabled.

Special care must be taken to prevent evaluation loops. In particular, if an expression depends on its assigned port, the expression must not be reevaluated when the only dependency that changed is that port's value.

The evaluation of a disabled port's value must be treated as an unavailable value.

Some functions may have additional evaluation triggers, indicated in their description.

Transform expressions, represented by the transform_write and transform_read attributes, should be evaluated only when reading from and writing to the port, respectively. However, all port writes and reads must pass through transform_write and transform_read, respectively.

Evaluation Result

The rules by which the result of an expression is determined depends on the expression kind. Below are the expression kinds and the way their value must be determined.

After evaluation of a value expression, the resulted value must be written to its associated port, but only if the evaluated value differs from the port's current value.

Data Types

When it comes to data types, conventions are similar to those of the C language: a boolean (logic) expression that needs to be interpreted as a number will have the values 0 or 1, corresponding to the false and true truth values, respectively. When numeric expressions need to be interpreted as booleans, the value 0 is considered false while any other value is considered true. When performing bitwise operations on floating point numbers, they are automatically rounded to their integer part.

Literal Value Expressions

Literal expressions are those whose result is immediate.

Literal numbers are expressed in base 10 and can optionally be preceded by a - minus sign. The decimal separator is the . dot character.

For literal boolean values, the following names are defined:

  • false is the logical false value
  • true is the logical true value

The special keyword unavailable represents a literal expression that is evaluated to unavailable.

Function Expressions

The result of a function expression is determined by calling the respective function with the given set of arguments. The actual result value depends on each function, as indicated in the Available Functions section.

If any argument of a function is unavailable, the function must be evaluated as unavailable as well, unless otherwise specified for that function.

Port Value Expressions

The result of a port value expression is the current value of that port when the expression is evaluated.

On asynchronous implementations where the evaluation of an expression is not an atomic operation, given that port values may change during the evaluation process, a snapshot of the required port values should be considered at the moment when the expression evaluation starts and used throughout the evaluation process. This ensures a consistent expression result.

One exception to the above rule is the self port expression, $, when not used as a transform expression: this type of expressions must always evaluate to the most recent known value of the port. When a port depends on itself, this ensures that the port is not accidentally written an older, outdated value of itself.

Port Reference Expressions

Result of a port reference must be unavailable, unless passed an argument to a function.

Available Functions

Most of the functions are mandatory for all implementations. There are a few functions, however, that must only be implemented by devices that satisfy certain conditions. Additional, implementation-specific functions may be supported by a device.

The following functions must be supported by all implementations that accept expressions, unless stated otherwise.

Arithmetic

  • ADD(v1, v2, ...)

    Takes at least two arguments and returns the sum of all arguments.

  • SUB(v1, v2)

    Returns the difference between v1 and v2.

  • MUL(v1, v2, ...)

    Takes at least two arguments and returns the result of the multiplication of all arguments.

  • DIV(v1, v2)

    Returns the division of v1 by v2.

  • MOD(v1, v2)

    Returns the modulo of the division of v1 by v2.

  • POW(b, e)

    Returns b to the power e.

Logic

  • AND(v1, v2, ...)

    Performs the logical and operation between all arguments.

  • OR(v1, v2, ...)

    Performs the logical or operation between all arguments.

  • NOT(v)

    Returns the logical negation of its argument.

  • XOR(v1, v2)

    Performs the logical exclusive or operation between v1 and v2.

Bitwise

  • BITAND(v1, v2)

    Performs the bitwise and operation between v1 and v2.

  • BITOR(v1, v2)

    Performs the bitwise or operation between v1 and v2.

  • BITNOT(v)

    Returns the bitwise complement of v.

  • BITXOR(v1, v2)

    Performs the bitwise exclusive or operation between v1 and v2.

  • SHL(v, k)

    Performs the bitwise shift left operation on v with k bits.

  • SHR(v, k)

    Performs the bitwise shift right operation on v with k bits.

Comparison

  • IF(c, t, f)

    If c is true, returns t; otherwise returns f.

  • EQ(v1, v2)

    Returns true if v1 is equal to v2; otherwise returns false.

  • GT(v1, v2)

    Returns true if v1 is greater than v2; otherwise returns false.

  • GTE(v1, v2)

    Returns true if v1 is greater than or equal to v2; otherwise returns false.

  • LT(v1, v2)

    Returns true if v1 is less than v2; otherwise returns false.

  • LTE(v1, v2)

    Returns true if v1 is less than or equal to v2; otherwise returns false.

Sign

  • ABS(v)

    Returns the absolute value of v.

  • SGN(v)

    Returns 1 if v is strictly positive, -1 if v is negative and 0 if it's 0.

Aggregation

  • MIN(v1, v2, ...)

    Takes at least two arguments and returns the minimum among them.

  • MAX(v1, v2, ...)

    Takes at least two arguments and returns the maximum among them.

  • AVG(v1, v2, ...)

    Takes at least two arguments and returns their arithmetic average (mean).

Rounding

  • FLOOR(v)

    Returns the largest integer value less than or equal to v.

  • CEIL(v)

    Returns the smallest integer value greater than or equal to v.

  • ROUND(v, d)

    Returns the value of v rounded to d digits after the decimal point.

Time

  • TIME()

    Takes no arguments and returns the current Unix timestamp, in seconds. For devices with no real date/time support, the uptime in seconds should be returned.

    This function should be evaluated once every second.

  • TIMEMS()

    Takes no arguments and returns the current Unix timestamp, in milliseconds. For devices with no real date/time support, the uptime in milliseconds should be returned.

    This function should be evaluated as often as possible.

Date

Functions in this category must only be implemented by devices with support for real date/time.

  • YEAR(t)

    Takes an optional argument representing a Unix timestamp, in seconds, and returns the corresponding year (e.g. 1985). If t is not supplied, returns the current year. The local real date/time should be considered instead of UTC, if available.

    If no argument is given, this function should be evaluated once every second.

  • MONTH(t)

    Takes an optional argument representing a Unix timestamp, in seconds, and returns the corresponding month (1 to 12). If t is not supplied, returns the current month. The local real date/time should be considered instead of UTC, if available.

    If no argument is given, this function should be evaluated once every second.

  • DAY(t)

    Takes an optional argument representing a Unix timestamp, in seconds, and returns the corresponding day (1 to 31). If t is not supplied, returns the current day. The local real date/time should be considered instead of UTC, if available.

    If no argument is given, this function should be evaluated once every second.

  • DOW(t)

    Takes an optional argument representing a Unix timestamp, in seconds, and returns the corresponding day of week (Monday is 0, Sunday is 6). If t is not supplied, returns the current day of week. The local real date/time should be considered instead of UTC, if available.

    If no argument is given, this function should be evaluated once every second.

  • LDOM(t)

    Takes an optional argument representing a Unix timestamp, in seconds, and returns the last day of the corresponding month (1 to 31). If t is not supplied, returns the last day of the current month. The local real date/time should be considered instead of UTC, if available.

    If no argument is given, this function should be evaluated once every second.

  • HOUR(t)

    Takes an optional argument representing a Unix timestamp, in seconds, and returns the corresponding hour (0 to 23). If t is not supplied, returns the current hour. The local real date/time should be considered instead of UTC, if available.

    If no argument is given, this function should be evaluated once every second.

  • MINUTE(t)

    Takes an optional argument representing a Unix timestamp, in seconds, and returns the corresponding minute (0 to 59). If t is not supplied, returns the current minute. The local real date/time should be considered instead of UTC, if available.

    If no argument is given, this function should be evaluated once every second.

  • SECOND(t)

    Takes an optional argument representing a Unix timestamp, in seconds, and returns the corresponding second (0 to 59). If t is not supplied, returns the current second. The local real date/time should be considered instead of UTC, if available.

    If no argument is given, this function should be evaluated once every second.

  • MILLISECOND()

    Takes no arguments and returns the current millisecond (0 to 999).

    This function should be evaluated as often as possible.

  • MINUTEDAY(t)

    Takes an optional argument representing a Unix timestamp, in seconds, and returns the corresponding minute inside the day (0 to 1439). If t is not supplied, returns the current minute of the day. The local real date/time should be considered instead of UTC, if available.

    If no argument is given, this function should be evaluated once every second.

  • SECONDDAY(t)

    Takes an optional argument representing a Unix timestamp, in seconds, and returns the corresponding second inside the day (0 to 86399). If t is not supplied, returns the current second of the day. The local real date/time should be considered instead of UTC, if available.

    If no argument is given, this function should be evaluated once every second.

  • DATE(ye, mo, da, ho, mi, se)

    Takes six arguments that represent the year, month, day, hour, minute and second that make up a full date with time. Returns the corresponding Unix timestamp, in seconds. The local real date/time should be considered instead of UTC, if available.

  • BOY(n)

    Takes an optional argument and returns the beginning of the n-th year, as Unix timestamp, in seconds, relative to the current year (for which n is 0). Negative n values go back in time. If n is not supplied, it must be considered 0 (i.e. current year). The local real date/time should be considered instead of UTC, if available.

    This function should be evaluated once every second.

  • BOM(n)

    Takes an optional argument and returns the beginning of the n-th month, as Unix timestamp, in seconds, relative to the current month (for which n is 0). Negative n values go back in time. Implementations must allow going beyond current year, both in the past and future. If n is not supplied, it must be considered 0 (i.e. current month). The local real date/time should be considered instead of UTC, if available.

    This function should be evaluated once every second.

  • BOW(n, s)

    Takes two optional arguments n and s and returns the beginning of the n-th week, as Unix timestamp, in seconds, relative to the current week (for which n is 0). Negative n values go back in time. Implementations must allow going beyond current year, both in the past and future. s indicates the start day of week, where Monday is 0 (default if not supplied). If n is not supplied, it must be considered 0 (i.e. current week). The local real date/time should be considered instead of UTC, if available.

    This function should be evaluated once every second.

  • BOD(n)

    Takes an optional argument n and returns the beginning of the n-th day, as Unix timestamp, in seconds, relative to the current day (for which n is 0). Negative n values go back in time. Implementations must allow going beyond current year, both in the past and future. If n is not supplied, it must be considered 0 (i.e. current day). The local real date/time should be considered instead of UTC, if available.

    This function should be evaluated once every second.

  • HMSINTERVAL(h1, m1, s1, h2, m2, s2)

    Takes six arguments representing the hour, minute and second that make up a start moment and the hour, minute and second that make up a stop moment, respectively, inside a full day. Returns true if current time is between the start moment and stop moment (inclusively), and false otherwise. The local real date/time should be considered instead of UTC, if available.

    This function should be evaluated once every second.

  • MDINTERVAL(m1, d1, m2, d2)

    Takes four arguments representing the month and day that make up a start date and the month and day that make up a stop date, respectively, inside a year. Returns true if current time is between the start date and stop date (inclusively), and false otherwise. The local real date/time should be considered instead of UTC, if available.

    This function should be evaluated once every second.

Time Processing

  • DELAY(v, t)

    Returns the value of v delayed by t milliseconds. The history buffer size is left to the implementation. The initial value of the function is v.

    This function should be evaluated as often as possible.

  • SAMPLE(v, t)

    Evaluates value v each t milliseconds and returns the last evaluated value. Sampling timer starts when expression state is reset.

    This function should be evaluated every t milliseconds.

  • FREEZE(v, t)

    Ignores changes to v for t milliseconds. Returns the last value of v that wasn't ignored. Ignoring timer starts whenever v changes and timer is off. Time t is evaluated only when timer starts. Initial value is v and timer is initially on.

    This function should be evaluated as often as possible while timer is active.

  • HELD(v, k, t)

    Returns true if v is held to the value k for at least t milliseconds. The result then becomes false with the first change of value v.

    This function should be evaluated as often as possible while v is equal to k for less than t milliseconds.

  • DERIV(v, t)

    Computes the discrete derivative of v. It returns the difference quotient from the last known v value by dividing the difference between current and last v value by the amount of time (in seconds, represented as floating point, with reasonably high precision) that has passed since the last v value evaluation.

    The function must be evaluated as often as indicated by the sampling interval t, in milliseconds.

    Initial result (when the last v value is not known) is 0.

  • INTEG(v, a, t)

    Computes the discrete integral of v, using a as previous accumulator value. It multiplies the arithmetic mean of current v and last v by the amount of time (in seconds, represented as floating point, with reasonably high precision) that has passed since the last v value evaluation, adds it to a and returns the result.

    The function must be evaluated as often as indicated by the sampling interval t, in milliseconds.

    Initial result (when the last v value is not known) is a.

  • FMAVG(v, n, t)

    Applies a moving average filter on the last n values of v. It returns the arithmetic mean of the most recent n values of v.

    The function must be evaluated as often as indicated by the sampling interval t, in milliseconds.

    Minimum n value is 1, while maximum is left to the implementation.

    If less than n values have been gathered so far, filtering is applied on all values.

  • FMEDIAN(v, n, t)

    Applies a median filter on the last n values of v. It returns the median value of the most recent n values of v.

    The function must be evaluated as often as indicated by the sampling interval t, in milliseconds.

    Minimum n value is 1, while maximum is left to the implementation.

    If less than n values have been gathered so far, filtering is applied on all values.

Various

  • AVAILABLE(v)

    Returns true if v is an available value and false otherwise.

  • DEFAULT(v, d)

    Returns v if v is available and d otherwise. Equivalent to IF(AVAILABLE(v), v, d).

  • RISING(v)

    Returns true if value v is strictly greater than the last value, or false otherwise.

    Initial result (when the last v value is not known) is false.

  • FALLING(v)

    Returns true if value v is strictly lower than the last value, or false otherwise.

    Initial result (when the last v value is not known) is false.

  • ACC(v, a)

    Accumulates changes in v, using a as previous accumulator value. Calculates the difference from current v and last evaluated value for v. Adds the difference to a and returns the value.

    Initial result (when the last v value is not known) is a.

  • ACCINC(v, a)

    Identical to ACC but considers only increases of value v and discards decreases.

  • HYST(v, t1, t2)

    Implements a hysteresis around value v; returned value is as follows:

    • if previously returned result was false, returns false if value v is below or equal to threshold t2, or true otherwise
    • if previously returned result was true, returns true if value v is above or equal to threshold t1, or false otherwise

    Previously returned value is initially considered false.

  • ONOFFAUTO(v, a)

    Helps building on/off/auto selection buttons, returning:

    • true if v is strictly greater than 0
    • false if v is strictly less than 0
    • a if v is equal to 0
  • SEQUENCE(v1, d1, ...)

    Returns the current value in a sequence of values v1, v2, ... with delays d1, d2, ... (in milliseconds). The current value is determined by evaluating the delay passed since an initial reference moment and comparing it to the cumulated delays in the sequence. The delay passed is computed modulo the sum of sequence delays, giving a repeat effect.

    The reference moment should be the moment when the expression state is reset.

    If the last delay is missing, it is considered 0.

    In a way, SEQUENCE is similar to the PATCH /ports/{id}/sequence API call when repeat is set to 0 (i.e. repeat indefinitely).

    This function must only be implemented by devices that support sequences (i.e. those that expose "sequences" in the flags attribute).

    This function should be evaluated as often as possible.

  • LUT(x, x1, y1, x2, y2, ...)

    Performs a lookup inside a lookup table made of pairs of (xi, yi) values. Searches for the closest value among x1, x2, ... and returns the corresponding yi value. Pairs in the lookup table may not be sorted. At least two pairs of values must be present (i.e. the function must recevie at least 5 arguments).

  • LUTLI(x, x1, y1, x2, y2, ...)

    Performs a lookup inside a lookup table made of pairs of (xi, yi) values. Searches for the two pairs whose xi values are the closest to x and are lower than or equal to and, respectively, higher than or equal to x. Returns the y value obtained by linear interpolation of x between the two pairs. Pairs in the lookup table may not be sorted. At least two pairs of values must be present (i.e. the function must recevie at least 5 arguments).

  • HISTORY(p, t, d)

    Returns the historical value of port p at Unix timestamp t, in seconds. Given that it's often unlikely for a sampled value to be found at exact given timestamp, the closest value to t must be returned, according to the argument d. Considering the difference diff(s) between sampling time of sample s and t:

    • if d is strictly positive, the sample for which diff(s) is positive or zero, minimal and less than d must be considered, if such a sample exists
    • if d is strictly negative, the sample for which diff(s) is negative or zero, maximal and greater than d must be considered, if such a sample exists
    • if d is 0, the sample for which diff(s) is positive or zero and minimal must be considered, if such a sample exists

    Considered samples must also include the current port's value, if available, associated to current time.

    If no sample matching above conditions has been found, the expression value is unavailable.

    Argument p must be a port reference.

    This function should be evaluated once every second.

    This function must only be implemented by devices that support history (i.e. those that expose "history" in the flags attribute).

Examples

The following expression represents the logically inverse value of port gpio1:

NOT($gpio1)

The following expression implements a flip-flop on a port, controlled usinggpio1; it will invert the value of the port each time gpio1 becomes true:

IF($gpio1, NOT($), $)

The following expression computes the double of the adc0, capping it at 1536:

MIN(MUL($adc0, 2), 1536)

Messages

The JSON Format

Messages transmitted between devices and consumers must be in JSON format, as defined by RFC 7159. Generally, it is preferred that a field that is not available/not applicable be absent from the message, rather than being sent with an associated null value. However, both the consumer and the device must be prepared to receive and parse null values and to treat corresponding fields as not available/not applicable.

Limits And Restrictions

In addition to the rules defined by the JSON format, the following general limits are imposed:

  • field names (dictionary keys) must contain only alpha-numeric values, underscores, points and dashes; they must start with a letter or an underscore
  • integer numbers are limited to the signed 32 bit representation
  • floating point numbers are limited to the IEEE 754 double-precision representation
  • the maximum length of a field name is 64 characters
  • the maximum length of a string value is 1024 characters
  • the maximum number of values in a list is 256
  • the maximum number of fields in a record is 256
  • the maximum length of a JSON-formatted message is 10240 bytes

The following regular expression can be used to validate field names:

^[_a-zA-Z][a-zA-Z0-9_.-]{0,63}$

These implicitly impose limits on all data sent via API function calls. In case these limits are exceeded in a message received by the device, the implementation may decide to accept the message as it is or reject it with an invalid request response.

JSON References

To reduce repeated content in message payloads, a mechanism similar to JSON Schema References may be used when generating JSON messages.

The special dictionary {"$ref": <pointer>} may appear instead of any JSON value and, upon reception, must be replaced by the referenced value. The value of <pointer> must be a string representing a URI reference to a value inside the received JSON document itself, starting with a fragment indicator # and followed by the pointer location, as described by RFC 6901.

The emitting party may choose to use JSON references when generating messages, but only in some limited cases, clearly indicated throughout this document. In these cases, the receiving party must be prepared to deal with such a message and to properly dereference any pointers.

When encountering an invalid JSON pointer syntax or a pointer whose target reference cannot be found inside the JSON document, the receiving party should consider the message invalid.

Value Validation

Ports and attributes have values which need to be validated before applied, when processing an API call. Regardless of their data types, values must be syntactically correct, according to JSON.

Values must respect the limits described in Limits.

The following restrictions apply, depending on the context of the value:

  • if a min restriction is defined and value data type is number, the value must be greater than or equal to the min value
  • if a max restriction is defined and value data type is number, the value must be less than or equal to the max value
  • if a min restriction is defined and value data type is string, the value length must be greater than or equal to the min value
  • if a max restriction is defined and value data type is string, the value length must be less than or equal to the max value
  • if the integer restriction is defined and true, the value must be an integer number
  • if the step restriction is defined, the value must be equal to min plus a multiple of step
  • if the choices restriction is defined, the value must be equal to one of the choice values in the choices list

Certain attributes, such as the device name or a port identifier have specific value restrictions mentioned in their definition. Implementations may choose to apply other specific validations on the values of additional attributes.

The HTTP Protocol

Devices and consumers exchange information using standard HTTP requests and responses, as defined by RFC 2616 (HTTP/1.1).

In most of the use cases of this API, the device plays the role of the HTTP server while the consumer plays the role of an HTTP client. There are however two scenarios where the device acts as an HTTP client connecting to a consumer which is in fact an HTTP server:

The terms request and response are always used throughout this document with regards to the HTTP protocol. The HTTP message in this context is a generic term for both of them.

A trailing slash in the request's path is optional and the request must not be affected by its presence or absence.

Status Codes

When the request has been successfully processed, 200 OK is returned and the response body contains the data described by each API function's documentation.

The server uses standard 4xx HTTP status codes to indicate various errors in requests and 5xx status codes to indicate a device-side error. In case of an error, in addition to the status code, the response will contain an error code that comes in the error field, in the body. The response body may contain other fields offering additional information.

The following status codes are common for all API functions:

  • 200 OK

    This status is returned after a successful non-creational API call that has a response body. The response body depends on the API function.

  • 201 OK

    This status is returned after a successful creational API call. The response body represents the created entity.

  • 202 Accepted

    This status is returned after a successful API call whose processing couldn't be completed right away. The response body depends on the API function.

  • 204 No Content

    This status is returned after a successful non-creational API call that has no response body.

  • 400 Bad Request

    This status indicates that the HTTP request could not be parsed. The following common values are defined for the error field:

    • "malformed-request" - the request is not a valid HTTP request message
    • "malformed-body" - the request body does not have the expected format (e.g. is not a valid JSON)
    • "missing-field" - a required field is missing from the request; additional field field indicates the name of the missing field
    • "invalid-field" - the value of the indicated field is invalid; additional field field indicates the name of the invalid field
    • "missing-header" - a required header is missing from the request; additional header field indicates the name of the missing header
    • "invalid-header" - the value of the indicated header is invalid; additional header field indicates the name of the invalid header
    • "invalid-request" - a general error indicating a request that doesn't respect the qToggle API rules
  • 401 Unauthorized

    This status indicates that the request must include authentication data. The error field value must be "authentication-required".

  • 403 Forbidden

    This status indicates that the operation on the requested resource is forbidden. The error field value must be "forbidden" and an additional required_level field must be present and state the required access level. See Authentication for more details.

  • 500 Internal Server Error

    This status indicates an unexpected device-side error. Error code must be set to "unexpected-error". The additional message field may provide details on the error.

  • 503 Service Unavailable

    This status indicates that the device cannot currently process the request. The following values are defined for the error field:

    • "busy" - device is currently busy and the request should be repeated later

API functions may have additional, specific status codes and/or error code, as mentioned in their documentation.

Methods

The following common HTTP methods are used:

  • GET requests don't have bodies and don't change anything on the device; they are only used to generate responses that retrieve information
  • POST requests carry information in their bodies and are used to create entities on the device (as well as other types of requests with side effect)
  • PATCH requests carry information in their bodies and are used to partially update entities on the device
  • PUT requests carry information in their bodies and are used to fully update entities on the device
  • DELETE requests don't have bodies and are used to delete entities on the device

An API function is defined by both the method and the requested path. For example, GET /device and PATCH /device are two different functions.

Headers

The following standard HTTP headers are employed in a device - consumer exchange:

Content-Length: <length> indicates the length of the body, in bytes; it must be present with all HTTP messages have a body.

Content-Type: application/json; charset=utf-8 indicates the format of the data carried by the body and must always indicate a JSON content; it must be present if and only if the HTTP message has a body.

Cache-Control: no-cache prevents the entities involved in the message transmission from caching the transferred data; it should be used with all HTTP responses.

Server: <server> may be included with an HTTP response to disclose the hostname (i.e. the device name, when the device acts as an HTTP server).

Authorization: Bearer <token> used by any HTTP message to supply authorization data (see Authentication)

Session-Id: <id> used by an HTTP message to indicate a particular session between device and consumer. The session id is chosen by the consumer and must be a string containing at most 32 alpha-numeric characters. The header is optional, unless otherwise stated. The recommended way of building such an id is to start with a consumer-specific text and append a part of the hexadecimal digest of a hash function, such as sha256, applied to the timestamp of the creation. This ensures theoretical uniqueness of the generated session id values.

Other standard headers may be present and will have their normal effect on the HTTP protocol, but they shouldn't affect the execution of the API call in any way.

Apart from the standard HTTP headers, the following qToggle-specific headers may be used:

Method: <method> used by the The Reverse API Calls Mechanism to specify the API method in the absence of a request line in the HTTP response message.

Path: <path> used by the The Reverse API Calls Mechanism to specify the API path in the absence of a request line in the HTTP response message.

Status: <code> <reason> used by the The Reverse API Calls Mechanism to specify the API return status code in the absence of a status line in the HTTP request message.

Query Arguments

With API functions that use the GET method, function arguments (if any) must transmitted as an URL-encoded query, as they have no body.

Parallel Requests

If a request arrives before the completion of the previous one, the implementation may choose to:

  • block the new request and respond with 503 Service Unavailable right away
  • queue the new request
  • execute the new request in parallel with the previous one, if there are no concurrency issues

HTTPS

Secure HTTP connections may be supported by devices. Whether a device may act as an HTTPS server is beyond the scope of this document, as it doesn't affect the API in any way. When it comes to Webhooks and The Reverse API Calls Mechanism, however, the device could be configured to connect to an HTTPS server.

Whether this is supported or not is advertised by device through the GET /device API function. If TLS support is enabled, in order for the device to use it, the "https" scheme should be used when configuring Webhooks or The Reverse API Calls Mechanism. If TLS is not supported by the device, the only accepted scheme should be "http".

Authentication

The authentication mechanism presented in this section is mandatory for all qToggle devices and consumers. The mechanism relies on passwords, which are ASCII strings with a maximum length of 32 characters.

The Authorization header is used to supply authentication data.

The standard HTTP 401 Unauthorized status must be used to indicate unauthenticated HTTP messages, accompanied by the error code "authentication-required".

The standard HTTP 403 Forbidden status must be used to indicate that an HTTP message that has been authenticated has been denied access to the requested resource. The response should be accompanied by the error code "forbidden". In addition to the error code, the field required_level must state the required access level (one of "admin", "normal" and "viewonly").

Access Levels

Three access levels are defined by qToggle, as follows (from the lowest to the highest):

  • viewonly, a view-only access level
  • normal, a normal user access level
  • admin, administrator access level

Usernames

The device may define multiple usernames associated with any of the three access levels. It must however define at least one username called admin having administrator access level.

Aside from an access level, each username must have an associated password. The device attribute admin_password represents the password associated to the admin username.

If the admin_password is empty, the device must consider any HTTP messages that have no Authorization header automatically authenticated as admin user.

Optionally, the device may define usernames normal and viewonly which must have normal and viewonly access levels. If the device defines any of these usernames, the associated optional password attributes (normal_password and viewonly_password) must be exposed as well.

The length of a username is limited to 32 characters.

Authorization Token

When authorization is required, in order for an HTTP message to be authorized, regardless of its origin and type, it must include the following Authorization header:

Authorization: Bearer <token>

Exception to this rule is the situation where admin_password attribute is empty; in this case, the Authorization header is no longer required and, in its absence, the message is automatically authenticated as admin.

The token is a JWT token, as defined by RFC 7519, whose header has:

  • alg set to "HS256"
  • typ set to "JWT"

The following claims are used by a qToggle JWT token:

  • usr - the username as string, whose presence and value depend on the context
  • ori - the token origin: "device" for device-originated tokens and "consumer" for consumer-originated tokens
  • iat - the current Unix timestamp, in seconds, as integer number, included if real date/time is available
  • iss - the issuer of the token, always set to "qToggle"

The token may contain other claims.

The token signature is obtained as follows:

signature = HMACSHA256(BASE64(<header>) + "." + BASE64(<payload>), HEX(SHA256(<password>)))

The HEX function transforms the result of the SHA256 digest into its lowercase hex representation, before being supplied to the HMACSHA256 function as a key. It is recommended that consumers store this hex hash value instead of the original password.

The final token is obtained using the following formula:

token = BASE64(<header>) + "." + BASE64(<payload>) + "." + BASE64(<signature>)

The BASE64 coding algorithm is defined in RFC 4648.

Token Usage And Validation

A token that does not have the alg and typ header fields set to values defined above must be considered invalid.

A token that does not have the iss claim set to "qToggle" must be considered invalid.

If the iat claim is present in the token and if the receiving party (device or consumer) has real date/time support, it must compare its current date/time to the one supplied in the iat field; in case the absolute difference between them is above an accepted time skew (typically a few minutes), the token must be considered invalid.

When the receiving party needs to authenticate the message, it must compute a local signature using the stored password (or hex hash), applying the formula described in Authorization Token on the incoming token's header and payload; if the incoming and locally computed signatures differ, then the token must be considered invalid.

Additional specific token validations rules apply to different types of HTTP messages, as follows.

Consumer API Call Request

A consumer API call request is an HTTP request that must be authenticated. An exception is however the GET /access function that can be called without supplying authorization information.

If the consumer wants to call an API function that requires (at least) a certain access level, it must supply a token including a username having that access level or any higher level, signed with the corresponding password.

A token that does not have the ori claim set to "consumer" must be considered invalid.

A token that does not have a usr (username) claim is considered invalid. If the device does not recognize the supplied username as a defined username, it must consider the token as invalid.

The device must validate the incoming token's signature using the supplied usr and the password associated to the username supplied in token's usr claim.

The API function GET /access can be used by the consumer to find out what access level is granted by a device for a certain set of credentials.

Device API Call Response

A device response to a normal API call is an HTTP response that should not be authenticated.

Device Webhooks Request

A device webhooks request is an HTTP request that must be authenticated.

The device must include in its HTTP request a token computed using the Webhooks password parameter as password and no usr claim.

A token that does not have the ori claim set to "device" must be considered invalid.

The consumer must validate the incoming token's signature using the password parameter.

Consumer Webhooks Response

A consumer response to a webhooks call is an HTTP response that should not be authenticated.

Device Reverse API Calls Initial Request

A device reverse API calls initial request is an HTTP request that must be authenticated.

The device must include a token computed using the The Reverse API Calls Mechanism password parameter as password and usr set to the device_id parameter.

A token that does not have the ori claim set to "device" must be considered invalid.

The consumer must validate the incoming token's signature using the password associated to the device id supplied with the token's usr claim.

Consumer Reverse API Calls Request

A consumer reverse API calls request is an HTTP response that must be authenticated.

If the consumer wants to call an API function that requires (at least) a certain access level, it must supply a token including a username having that access level or any higher level, signed with the corresponding password.

A token that does not have the ori claim set to "consumer" must be considered invalid.

A token that does not have a usr (username) claim is considered invalid. If the device does not recognize the supplied username as a defined username, it must consider the token invalid.

The device must validate the incoming token's signature using the password associated to the username supplied with the token's usr claim.

Device Reverse API Calls Response

A device reverse API calls response is an HTTP request that must be authenticated.

The device must include a token computed using the The Reverse API Calls Mechanism password parameter as password and usr set to the device_id parameter.

A token that does not have the ori claim set to "device" must be considered invalid.

The consumer must validate the incoming token's signature using the password associated to the device id supplied with the token's usr claim.

Notifications

In order for the consumers to be notified of various events that occur on the device side, two notification mechanisms are defined by the qToggle API: Listening and Webhooks. Both mechanisms are optional and in their absence, the consumer may implement a polling routine to determine any changes that took place on the device.

Events

Each event generated by the device has a type; each type of event has its own parameters. Aside from the event types documented below, a device may trigger events of custom types with custom parameters, whose definition and meaning are beyond of the scope of this document.

value-change

A value-change event is triggered when the value of a port changes. Determining when a port value has changed is done by the rules described in Port Value Changes. This event has the following parameters:

{
    "id": string,
    "value": boolean|number,
    "old": boolean|number
}
  • id represents the identifier of the port whose value has changed
  • value represents the new value of the port
  • old_value represents the old value of the port

Ports having the internal attribute set to true should not trigger value-change events.

This event type requires at least viewonly access level.

port-update

A port-update event is triggered whenever:

  • an attribute of a port is updated using PATCH /ports/{id} or by any other means
  • a new port attribute is added
  • an existing port attribute is removed
  • the definition of a non-standard port attribute has changed

This event has the same parameters as one of the list elements in the response of the GET /ports API function call.

The device may choose not to trigger this event for some attributes that are very susceptible and could generate an unnecessary amount of events.

This event type requires at least viewonly access level.

port-add

A port-add event is triggered a port is added to the device. This event might be triggered by a device at startup, when loading ports; for ports that are already present, consumers should handle port-add events just like a port-update event.

This event has the same parameters as one of the list elements in the response of the GET /ports API function call.

This event type requires at least viewonly access level.

port-remove

A port-remove event is triggered when a port is removed from the device.

This event has the following parameters:

{
    "id": string
}

This event type requires at least viewonly access level.

device-update

A device-update event is triggered whenever:

  • an attribute of the device is updated using PATCH /device or by other means
  • a new device attribute is added
  • when an existing device attribute is removed
  • the definition of a non-standard device attribute has changed

This event has the same parameters as the response of the GET /device API function call.

The device may choose not to trigger this event for some attributes that are very susceptible and could generate an unnecessary amount of events.

This event type requires admin access level.

full-update

A full-update event may be triggered by device to inform consumers that a full update (device and all ports) is required. It is recommended that this event be used when a device would accumulate many events within a short interval of time (such as during a provisioning procedure) and, instead of triggering individual events, it tells consumers to do a full reload.

Upon receiving full-update, a consumer should:

  • fetch and update cached device attributes using GET /device
  • fetch and update cached ports and port attributes using GET /ports

This event has no parameters.

This event type requires viewonly access level.

Listening

The optional GET /listen API function allows implementing so-called long-polling requests between a consumer and the device. The consumer initiates a request to the device and will wait for an event. The request is answered when an event occurs, or after a timeout which used as a keep-alive mechanism. The consumer should then repeat the listen request as soon as possible.

Listen Sessions

A listening session is made up of repetitive listen requests issued by the same consumer with relatively small pauses between them. A consumer must identify its session at every listen request using the Session-Id header. Ideally, listen requests should have no pause between them, but in reality, the network latency as well as the non-negligible processing time will induce small systematic pauses during which events may be lost. The role of these sessions is to allow the device to store events between successive listen requests and to serve them to the consumer's next listen request.

The session must be preserved after the listen request has been responded for a period of time equal to at least the session's timeout.

The maximum number of simultaneous listen sessions is left to the implementation and will also dictate the maximum number of supported parallel listen requests. If this limit has been reached and a listen request for a new session is received, the device should choose one of the existing sessions and close it by immediately responding to the corresponding request.

The device must keep track of the access level corresponding to each session. The access level is updated with every listen request. Only events that are accessible to the session's access level will be served to the respective session.

Event Queues

The device must keep a queue of pending events for each session. When a new event occurs, for sessions with a current listening connection, the event must be served right away. For sessions that have no current listen connection, the device must place new events into the session's queue; queued events will be all served as soon as the consumer performs the next listen request (with the same Session-Id header). The capacity of a queue should be equal to at least the number of available ports on the device. When the capacity of the queue is reached, the oldest (first) event is dropped. When a session expires, the entire queue is dropped.

Webhooks

Webhooks are an optional functionality that can be used by a device to notify consumers about events. When working with webhooks, the HTTP server/client roles are reversed between the device and the consumer: the device becomes the HTTP client and the consumer acts as an HTTP server.

Webhooks are particularly useful for scenarios where devices simply need to push events to the consumer without having a permanent connection with it.

Only one consumer per device can be configured to receive webhooks notifications. All Events must be covered by webhooks. At startup, the device may choose to send an initial round of webhooks notifications that cover all ports (and the device itself) so that it can inform the consumer of its state.

The corresponding API functions are:

Parameters

The webhooks configuration is defined by the following parameters required to make the HTTP request:

  • scheme determines the transport protocol used and can be either "http" or "https"; for devices that don't support TLS, the only accepted value should be "http".
  • host is the consumer's host (the HTTP server in this case); this can be either an IP address or a domain name.
  • port is the consumer's TCP port.
  • path is the request path (including any query arguments).
  • password is a password string shared between the consumer and the device, used to build the Authorization Token.
  • events is a list of event types to be sent via webhooks. See Events for available event types.
  • timeout is the timeout in seconds to wait for a response from the consumer. Valid values range from 1 to 3600.
  • retries is the number of retries in case of failure. Valid values range from 0 to 10.

Request

Upon an event, the device issues an HTTP request according to the webhook parameters. The connection to the server is made using the given scheme, host and port. The request will always have the POST method. It will be made at the given path.

See Device Webhooks Request for authentication details.

The Content-Type header must be set to application/json; charset=utf-8. The body must have the following format:

{
    "type": string,
    "params": any
}

The device must use the configured timeout parameter to determine how long to wait for a response from the consumer. The device must retry sending the webhook request in case of failures, according to the retries parameter. Failures are any timeouts, responses with statuses that are not 200 and other network errors. Setting the retries parameter to 0 disables the retry mechanism.

The request body may contain JSON References.

Event Queues

Since events might be generated with a faster rate than webhook requests can be acknowledged, the device must keep a queue of events to be sent to the consumer via webhooks. New events must be pushed back to the queue and they must be served one by one to the consumer via a webhook request. Webhook requests must never be sent simultaneously. An event is removed from the queue when a successful webhook response is received or when the number of retries is reached. When the capacity of the queue is reached, the oldest (first) event is dropped. The capacity of the webhook events queue is left to the implementation.

Example

Device's request indicating that the value of the port named "gpio" just became true:

POST /qtoggle/notify/ HTTP/1.1
Host: consumer.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm9yaSI6ImRldmljZSIsImlzcyI6InFUb2dnbGUifQ.NTQZ9QrVXFBv1C3YxqknuzWSQOiS-N0btTk9LhyiQ9A
Content-Type: application/json; charset=utf-8
Content-Length: n

{
    "type": "value-change",
    "params": {
        "id": "gpio0",
        "value": true,
        "old_value": false
    }
}

The Reverse API Calls Mechanism

The reverse API calls mechanism is an optional functionality that allows calling any API function via a reverse HTTP connection. The HTTP roles of the device and consumer are reversed: the device (the HTTP client) will continuously have a waiting request to the consumer (the HTTP server).

The corresponding API functions are:

When called through a reverse HTTP connection, the GET /listen API function must be rejected with 404 Not Found and an error set to of "no-such-function".

Parameters

The parameters required for the reverse API call are:

  • scheme determines the transport protocol used and can be either "http" or "https"; for devices that don't support TLS, the only accepted value should be "http".
  • host is the consumer's host (HTTP server in this case); this can be either an IP address or a domain name.
  • port is the consumer's TCP port.
  • path is the path to request (including any fixed query arguments).
  • device_id is a (normally unique) device identifier shared between the consumer and the device, used to build the Authorization Token; field name restrictions apply to this field.
  • password is a password string shared between the consumer and the device, used to build the Authorization Token.
  • timeout specifies the timeout in seconds to wait for a response from the consumer. Valid values range from 1 to 3600.

Reverse API Sessions

If the mechanism is enabled, the device will issue an initial HTTP request to the defined consumer host and will wait for an HTTP response. Upon HTTP response, the device will process it and will immediately issue a new answer HTTP request. If timeout is reached, the device will issue a new initial HTTP request. The timeout is used as a connectivity keep-alive and may also be used to determine the online status of a device on the consumer side. This is what constitutes a reverse API session.

When a session ends (an empty HTTP response is received, timeout is reached or an error occurs), the device must immediately start a new session by issuing a new initial HTTP request.

No more than one session per device must be allowed by the consumer. Upon receiving a valid and authenticated initial HTTP request from a device, the consumer must immediately respond to any other waiting initial HTTP request coming from the same device, identified by its device_id. The response must be an empty HTTP response carrying no API call details.

Headers

The headers as well as the content of the requests and responses are reversed in the case of reverse API calls. Their roles are in fact reversed: the response of a consumer will carry the information that a request would carry in a normal API call scenario, while a device's answer HTTP request will carry what an HTTP response would carry in a normal API call scenario. The initial HTTP request doesn't carry any information; it has the only role of initiating and keeping the connectivity alive between the device and the consumer.

Given that an HTTP response doesn't have a request line (used to identify the API function), two special HTTP headers are used with the response, in place of a request line:

Method: <method> specifies the API method.

Path: <path> specifies the API path (including any query arguments).

Similarly, given that an HTTP request doesn't have a status line (used to tell whether the API call succeeded or not), the following HTTP header must be used with answer requests, in place of a status line:

Status: <code> <reason> is used to indicate the API return status code (and associated reason).

Both the answer HTTP request and the HTTP response must contain the Session-Id header.

Device Initial Request

The initial HTTP request is used by a device to start a reverse API session. It is a simple POST request with no body and no particular headers.

Device Answer Request

The device's answer HTTP request is used by the device to transmit the result of a reverse API call. It is a POST request that must include theStatus and Session-Id headers, as described above. Its body represents the API response body.

The Session-Id value must be identical to that of the value from the previously received consumer's HTTP request.

Consumer Response

The consumer's HTTP response is used by the consumer to request an API call and must contain the Method, Path and Session-Id headers, as described above. Its body represents the API request body. The status code must always be 200 OK for an HTTP response corresponding to a valid and authenticated initial request. 401 Unauthorized must be used if the authentication of the initial request fails.

The Session-Id is used to match the API call request with its following response.

Empty Consumer Response

An empty consumer response is used by the consumer to end a reverse API session. It should not contain any particular headers and its body must always be empty. The status code must be 204 No Content.

Example

Device's initial request:

POST /qtoggle/reverse HTTP/1.1
Host: consumer.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiI2MjFmYTNmYiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiZGV2aWNlIiwiaXNzIjoicVRvZ2dsZSJ9.TZmRujrxubcm57ZW02rWv_jqMF50NgCFVno2av90WjU
Content-Length: 0

Consumer's response calling the PATCH /ports/{id}/value function:

HTTP/1.1 200 OK
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y
Content-Type: application/json; charset=utf-8
Content-Length: n
Method: PATCH
Path: /ports/gpio0/value
Session-Id: 6fb8387c190ab54c

false

Device's answer request:

POST /qtoggle/reverse HTTP/1.1
Host: consumer.example.com
Connection: close
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiI2MjFmYTNmYiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiZGV2aWNlIiwiaXNzIjoicVRvZ2dsZSJ9.TZmRujrxubcm57ZW02rWv_jqMF50NgCFVno2av90WjU
Status: 204 No Content
Session-Id: 6fb8387c190ab54c

Backup & Restore

Backup

Backing up the entire configuration of a device should be possible using individual GET requests to selected endpoints. The resulted JSON content should be stored in separate properties of a root backup dictionary, in a JSON backup file.

The following standard endpoints should be used for configuration backup:

In addition to these endpoints, a device may implement a GET /backup/endpoints API call that indicates extra endpoints that need to be backed up. Devices exposing the "backup" flag must implement this API call.

The consumer performing the backup should follow these steps:

  1. Issue a GET /backup/endpoints request to find the extra backup endpoints, if device exposes "backup" flag.
  2. Issue separate GET requests to the standard endpoints above.
  3. Issue separate GET requests to all extra backup endpoints.
  4. Put together a JSON file with a root dictionary having a property for each backed up endpoint

The backup procedure should stop at first erroneous request, failing to produce a backup file, thus preventing incomplete backups.

For each backed up endpoint, the key in the backup JSON file should be the endpoint without the leading / slash (e.g. "device" for /device); any non-alpha-numeric characters (including intermediate / slashes) should be replaced with an _ underscore. The value should be the response body returned by the respective request.

Restore

Restoring the entire configuration of a device should be possible using individual PATCH/PUT requests to selected endpoints; the actual HTTP method used depends on the endpoint. The content used for these requests should be taken from a JSON backup file previously obtained with a backup procedure.

For a device that exposes the "backup" flag, the consumer performing the restore should follow these steps:

  1. Consider the following standard, backup-specific endpoints:

  2. Issue a GET /backup/endpoints request to find the extra backup endpoints.

  3. Sort all gathered endpoints by their order, ascending.

  4. Issue separate PATCH/PUT requests to all gathered endpoints that have a corresponding property present in the backup file, using the data supplied in the backup file; requests order found above must be respected.

For devices that don't expose the "backup" flag, the consumer may attempt to restore the configuration by selectively supplying only modifiable device and port attributes using the corresponding standard endpoints PATCH /device and PATCH /ports/{id}.

The restore procedure should stop at first erroneous request, indicating a restore failure, allowing the consumer to retry. The consumer should use the POST /reset API call to reset the device at then end of the restore procedure.

The consumer may choose to skip parts of the backup file when restoring data. The consumer could skip entire sections or could choose to skip certain device or port attributes. In fact the consumer should prevent changing network parameters (such as Wi-Fi or IP) when restoring a configuration.

History

The optional history mechanism allows saving samples of port values for later inspection, visualization or traceability purposes. The history_interval and history_retention attributes of a port control the sampling interval and data retention duration, respectively.

Devices that expose the "history" flag must fully implement the history attributes above and the following endpoints:

For each port that has history enabled (attribute history_interval is not 0), a device with history support must sample the port value according to its history_interval and save it, along with the current timestamp (with milliseconds precision) to a persisted storage.

The device should skip adding a history record for port values that are unavailable when sampled. It should also skip adding history records for disabled ports.

For each port with limited history retention (attribute history_retention is greater than 0), a device with history support must clean up history records with timestamps older than current time minus indicated retention. It is up to the implementation to decide how often the cleanup process is done.

API Functions

A request to an unknown method + path combination must be answered with 404 Not Found and an error field set to "no-such-function". The same applies to optional functions defined by this API which are not supported by the implementation.

Responses documented with each API functions correspond to a successful execution of the API call. Errors are signaled with the status codes specific to each function or with the common Status Codes.

Device Management

GET /device

Returns all the device attributes (both standard and additional), along with all definitions for additional attributes.

This API function requires admin access level.

Response has the following body:

{
    "name": string,
    "display_name": string,
    "version": string,
    "api_version": string,
    "vendor": string,
    ...,
    "definitions": {
        "<name>": {
            "display_name": string,
            "description": string,
            "type": string,
            "modifiable": boolean,
            "unit": string,
            "min": number,
            "max": number,
            "integer": boolean,
            "step": number,
            "choices": [
                {
                    "value": number|string,
                    "display_name": string
                },
                ...
            ]
        },
        ...
    }
}

Each field in the response represents an attribute with its associated value. For more details on the meaning of these attributes, see Standard Device Attributes.

An exception is the definitions field which represents the definitions of all additional attributes. Inside the definitions dictionary, the record associated to each key represents an attribute with the name indicated by the key. Fields unit, min, max, integer, step and choices are optional; for their meaning, see Attributes.

Definitions for Common Device Attributes should not be included in the response, since their definitions are imposed by this document.

The response body may contain JSON References.

Example:

Consumer's request:

GET /device HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y

Device's response:

HTTP/1.1 200 OK
Connection: close
Cache-Control: no-cache
Content-Type: application/json; charset=utf-8
Content-Length: n

{
    "name": "lamp1",
    "display_name": "First lamp switch device",
    "version": "3.14.15f",
    "api_version": "1.0",
    "vendor": "qtoggle/qtoggleserver",
    "admin_password": "",
    "flags": ["firmware", "listen"],
    "frequency": "40",
    "sleep_timeout": "300",
    "definitions": {
        "frequency": {
            "display_name": "Frequency",
            "description": "The operating frequency of the device",
            "type": "number",
            "modifiable": true,
            "unit": "MHz",
            "choices": [
                {
                    "value": 20
                },
                {
                    "value": 40
                },
                {
                    "value": 80
                }
            ]
        },
        "sleep_timeout": {
            "display_name": "Sleep Timeout",
            "description": "Interval of inactivity after which device enters sleep mode",
            "type": "number",
            "modifiable": true,
            "unit": "seconds",
            "min": 1,
            "max": 3600,
            "integer": true
        }
    }
}

PUT /device

Replaces the entire device configuration. Device must prevent resetting/rebooting/reconnecting as a consequence to updating device attributes, regardless of any attribute with reconnect field set.

This is an optional API function and may not be available in all implementations. The consumer may use GET /device to find out whether this is supported or not by the device by checking the presence of the "backup" value in the flags device attribute.

This API function requires admin access level.

Request has the same body format as the one returned by GET /device.

The device must first restore all device attributes to the initial state, to the possible extent; password attributes and network-related attributes should be excluded from resetting to initial state.

The device must then update all modifiable attributes for which values have been supplied, with a few exceptions noted below. Any field that does not belong to a modifiable attribute, whether the respective attribute exists or not, must be silently ignored. However, any error in applying a modifiable attribute must immediately stop the processing of request and must result in a corresponding error response.

Values supplied for password attributes admin_password, normal_password and viewonly_password must be ignored. Nevertheless, the device should consider the following password fields instead: admin_password_hash, normal_password_hash and viewonly_password_hash, representing the sha256 lowercase hex digest of their respective passwords. For each of them, if present, the device should update the internal password hash, assuming that hashes are kept instead of clear-text passwords.

Value supplied for the date attribute should be ignored.

Response has no body.

The possible status codes in case of an error are the status codes of PATCH /device.

Example:

Consumer's request:

PUT /device HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y
Content-Type: application/json; charset=utf-8
Content-Length: n

{
    "name": "lamp1",
    "display_name": "First lamp switch device",
    "admin_password_hash": "6cad63595165fc39c9a3c715b874d406758bccb91e98106db8cd21da3150f658",
    "frequency": "40",
    "sleep_timeout": "300"
}

Device's response:

HTTP/1.1 204 No Content
Connection: close

PATCH /device

Updates one or more device attributes. Only the specified attributes will be updated.

When deciding the order of applying the attributes, the device must take into account that (the presence of) some attributes may depend on the values of other attributes.

This API function requires admin access level.

Request has the following body:

{
    "<attr>": any,
    ...
}

For more details, see Standard Device Attributes.

Response has no body.

Particular status codes:

  • 400 Bad Request - error field values:
    • "attribute-not-modifiable" - this attribute cannot be modified; additional attribute field indicates the name of the attribute
    • "no-such-attribute" - the attribute does not exist; additional attribute field indicates the name of the attribute
    • "invalid-field" - the value supplied for one of the attributes is invalid (see Value Validation); additional field field indicates the name of the attribute

Example:

Consumer's request:

PATCH /device HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y
Content-Type: application/json; charset=utf-8
Content-Length: n

{
    "name": "lamp1",
    "display_name": "First lamp switch device",
    "admin_password": "test1234",
    "frequency": 40,
    "sleep_timeout": 300
}

Device's response:

HTTP/1.1 204 No Content
Connection: close

POST /reset

Resets (restarts) the device. Optionally restores the factory defaults.

The actual reset should be delayed by a few seconds so that the response can be sent and received by the consumer. No other incoming request should be responded during this period. The consumer could employ an HTTP polling algorithm with a short timeout to determine if device got back online. It could use the GET /device API function for polling purposes.

This API function requires admin access level.

Request has the following body:

{
    "factory": boolean
}
  • factory is optional and, if present and set to true, will reset the device to its factory defaults.

Response has no body.

Example:

Consumer's request:

POST /reset HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y
Content-Type: application/json; charset=utf-8
Content-Length: n

{
    "factory": false
}

Device's response:

HTTP/1.1 204 No Content
Connection: close

GET /firmware

Returns the current as well as the most recent available firmware version.

This is an optional API function and may not be available in all implementations. The consumer may use GET /device to find out whether this is supported or not by the device by checking the presence of the "firmware" value in the flags device attribute.

This API call should be available throughout the firmware update process, as long as possible.

This API function requires admin access level.

Response has the following body:

{
    "version": string,
    "latest_version": string,
    "latest_date": string,
    "latest_url": string
    "status": string
}
  • version is the current device firmware version, the same as the version attribute.
  • latest_version is the latest available version.
  • latest_date represents the latest release date and must have the following format: yyyy-mm-dd.
  • latest_url is the full URL to the latest firmware.
  • status is the current firmware update process status; possible values are:
    • "idle" - update process not running
    • "checking" - possibility to download requested firmware is being checked
    • "downloading" - firmware is being downloaded
    • "extracting" - firmware is being extracted
    • "validating" - firmware is being validated
    • "flashing" - firmware is being flashed
    • "restarting" - device will soon restart
    • "error" - an error occurred during the firmware update process

Fields latest_version, latest_date and latest_url may not be included in case respective information cannot be obtained. They should be omitted when status is not "idle" or "error".

Version string format as well as the comparison between versions is an implementation choice.

Example:

Consumer's request:

GET /firmware HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y

Device's response:

HTTP/1.1 200 OK
Connection: close
Cache-Control: no-cache
Content-Type: application/json; charset=utf-8
Content-Length: n

{
    "version": "3.12c",
    "latest_version": "3.14f",
    "latest_date": "1999-12-31",
    "latest_url": "https://firmware.qtoggle.com/firmware/esp8266/3.14.15/8ec42c61",
    "status": "downloading"
}

PATCH /firmware

Updates the firmware to a specific version.

This is an optional API function and may not be available in all implementations. The consumer may use GET /device to find out whether this is supported or not by the device by checking the presence of the "firmware" value in the flags device attribute.

The consumer should use an HTTP polling algorithm by repeatedly requesting GET /firmware with a short timeout to determine the status of the firmware update process.

The device may choose to reject API calls during the firmware update process by responding with 503 Service Unavailable and an error set to "busy". It should however allow requests to GET /firmware. The GET /firmware API call must return a status other than "idle" immediately after PATCH /firmware.

This API function requires admin access level.

Request has the following body:

{
    "version": string,
    "url": string
}
  • version field should contain the version previously obtained with a GET /firmware or any other desired version. Updating the firmware to any other specific version that is available should also be possible.
  • url field should be the complete URL to a firmware; this can be used to force the firmware update from a specific firmware location.

Both fields are optional, but one of them must be present. If both are present, url takes precedence.

Response has no body.

Particular status codes:

  • 404 Not Found - error field values:
    • "no-such-version" - the specified version cannot be found
  • 503 Service Unavailable - error field values:
    • "busy" - firmware update process is already running

Example:

Consumer's request:

PATCH /firmware HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y
Content-Type: application/json; charset=utf-8
Content-Length: n

{
    "version": "3.14.15-beta.9"
}

Device's response:

HTTP/1.1 204 No Content
Connection: close

GET /access

Returns the access level that is granted for the credentials used to sign the request.

This API function does not require any specific access level. This API function should never return 401 or 403 HTTP status codes.

Response has the following body:

{
    "level": string
}
  • level is one of admin, normal, viewonly and none.

Example:

Consumer's request:

GET /access HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y

Device's response:

HTTP/1.1 200 OK
Connection: close
Cache-Control: no-cache
Content-Type: application/json; charset=utf-8
Content-Length: n

{
    "level": "admin"
}

GET /backup/endpoints

Returns details about extra endpoints that need to be backed up (and restored), in addition to the standard endpoints, when performing Backup & Restore procedures.

This is an optional API function and may not be available in all implementations. The consumer may use GET /device to find out whether this is supported or not by the device by checking the presence of the "backup" value in the flags device attribute.

This API function requires admin access level.

Response has the following body:

[
    {
        "path": string,
        "display_name": string,
        "restore_method": string,
        "order": number
    },
    ...
]

For each extra endpoint that requires backup, the device must return an entry in the endpoints list, with the following details:

  • path - the API endpoint path, relative to the API root, e.g. "/system/configuration"
  • display_name - a name to be used when displaying the backup section to the user
  • restore_method - the HTTP method to be used for restore: PATCH or PUT
  • order - the order in the list of requests performed during a Restore procedure

If no extra endpoints, besides the standard ones, should be part of a backup/restore procedure, the device must simply return an empty list.

Example:

Consumer's request:

GET /backup/endpoints HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y

Device's response:

HTTP/1.1 200 OK
Connection: close
Cache-Control: no-cache
Content-Type: application/json; charset=utf-8
Content-Length: n

[
    {
        "path": "/peripherals",
        "display_name": "Peripherals",
        "restore_method": "PUT",
        "order": 5
    },
    {
        "path": "/system",
        "display_name": "System Configuration",
        "restore_method": "PATCH",
        "order": 65
    }
]

Port Management

GET /ports

Returns the list of available ports, along with their values and attributes.

This API function requires at least viewonly access level.

Response has the following body:

[
    {
        "id": string,
        "display_name": string,
        "type": string,
        "unit": string,
        "writable": boolean,
        "enabled": boolean,
        "value": any,
        "pending_value": any,
        ...,
        "definitions": {
            "<name>": {
                "display_name": string,
                "description": string,
                "type": string,
                "modifiable": boolean,
                "unit": string,
                "min": number,
                "max": number,
                "integer": boolean,
                "step": number,
                "choices": [
                    {
                        "value": number|string,
                        "display_name": string
                    },
                    ...
                ]
            },
            ...
        }
    },
    ...
]

Each record in the list represents a port. Each field in the record, with the exception of value, pending_value and definitions, represents an attribute name with its value. For more details on the meaning of these attributes, see Standard Port Attributes.

The special field value represents the current value of the port. For disabled ports, or if the value is unavailable, the field value should be null.

The special field pending_value represents the most recent pending value of the port, as described in Port Writing Process. For disabled ports, or if there is no pending value, the field pending_value should be null.

The special field definitions represents the definitions of all additional attributes. Inside the definitions dictionary, the record associated to each key represents an attribute with the name indicated by the key. Fields unit, min, max, integer, step and choices are optional; for their meaning, see Attributes.

Definitions for Common Port Attributes should not be included in the response, since their definitions are imposed by this document.

The response body may contain JSON References.

Example:

Consumer's request:

GET /ports HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y

Device's response:

HTTP/1.1 200 OK
Connection: close
Cache-Control: no-cache
Content-Type: application/json; charset=utf-8
Content-Length: n

[
    {
        "id": "gpio0",
        "display_name": "GPIO 0",
        "type": "boolean",
        "writable": true,
        "enabled": true,
        "value": false,
        "pending_value": true,
        "pull_up": true,
        "debounce": false,
        "definitions": {
            "pull_up": {
                "display_name": Pull-up",
                "description": "Controls the pull-up resistor",
                "type": "boolean",
                "modifiable": true
            },
            "debounce": {
                "display_name": Debounce Filter",
                "description": "Enables the debounce filter",
                "type": "boolean",
                "modifiable": true
            }
        }
    },
    {
        "id": "adc0",
        "display_name": "Analog-Digital Converter Input 0",
        "type": "number",
        "writable": false,
        "enabled": true,
        "value": 1536,
        "pending_value": null,
        "definitions": {}
    }
]

PATCH /ports/{id}

Updates one or more attributes of a given port. Only the specified attributes will be updated.

When deciding the order of applying the attributes, the device must take into account that (the presence of) some attributes may depend on the values of other attributes.

This API function requires admin access level.

Path has the following arguments:

  • id specifies the identifier of the desired port

Request has the following body:

{
    "<attr>": any,
    ...
}

For more details on attributes, see Standard Port Attributes.

Response has no body.

Particular status codes:

  • 400 Bad Request - error field values:
    • "attribute-not-modifiable" - this attribute cannot be modified; additional attribute field indicates the name of the attribute
    • "no-such-attribute" - the attribute does not exist; additional attribute field indicates the name of the attribute
    • "invalid-field" - the value supplied for one of the attributes is invalid (see Value Validation); additional field field indicates the name of the attribute
  • 404 Not Found - error field values:
    • "no-such-port" - a port with the given id does not exist
  • 502 Bad Gateway - error field values:
    • "port-error" - error communicating with the port (e.g. a hardware fault); additional message field may show details about the error
  • 504 Gateway Timeout - error field values:
    • "port-timeout" - communication with the port timed out

Example:

Consumer's request:

PATCH /ports/gpio0 HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y
Content-Type: application/json; charset=utf-8
Content-Length: n

{
    "display_name": "Lamp LED A",
    "pull_up": true
}

Device's response:

HTTP/1.1 204 No Content
Connection: close

POST /ports

Adds a new virtual port to the device. The port must have the attribute enabled set to true right after being added. The initial value must be unavailable (null).

This is an optional API function and may not be available in all implementations. The consumer may use GET /device to find out whether this is supported or not by the device by inspecting the attribute virtual_ports.

This API function requires admin access level.

Request has the following body:

{
    "id": string,
    "type": string,
    "min": number,
    "max": number,
    "integer": boolean,
    "step": number,
    "choices": [
        {
            "value": number,
            "display_name": string
        },
        ...
    ]
}
  • id is the identifier of the new port
  • type is the port type
  • min, max, integer, step and choices are optional, accepted only for number ports and represent the values of the corresponding port attributes

Response has the same body as one of the list elements in the response of the GET /ports API function call, representing the attributes of the newly created port.

The response body may contain JSON References.

Particular status codes:

  • 400 Bad Request - error field values:
    • "duplicate-port" - a port with this identifier already exists
    • "invalid-field" - one of the fields is invalid; additional field field indicates the name of the field:
      • "id" - the given id is invalid
      • "type" - the given type is not one of the supported types
      • "min" - the given min is not a number
      • "max" - the given max is not a number of is less than given min
      • "step" - the given step is not a number
      • "choices" - the given choices is not a list of dictionaries with expected fields or is too long
    • "too-many-ports" - the maximum number of supported virtual ports has been reached

Example:

Consumer's request:

POST /ports HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y
Content-Type: application/json; charset=utf-8
Content-Length: n

{
    "id": "vport_a",
    "type": "boolean"
}

Device's response:

HTTP/1.1 201 Created
Connection: close
Cache-Control: no-cache
Content-Type: application/json; charset=utf-8
Content-Length: n

{
    "id": "vport_a",
    "display_name": "",
    "type": "boolean",
    "unit": "",
    "writable": true,
    "enabled": true,
    "value": false,
    "pending_value": null,
    "definitions": {}
}

PUT /ports

Replaces the entire ports configuration, removing, creating and updating ports along with their attributes, as necessary. Device must prevent resetting/rebooting/reconnecting as a consequence to updating port attributes, regardless of any attribute with reconnect field set.

This is an optional API function and may not be available in all implementations. The consumer may use GET /device to find out whether this is supported or not by the device by checking the presence of the "backup" value in the flags device attribute.

This API function requires admin access level.

Request has the same body format as the one returned by GET /ports.

The device must begin with removal of all virtual ports. The device must restore all attributes of all ports to the initial state, to the possible extent. Then, for each element in the list of ports, which is a dictionary with port attributes, the device must do the following:

  • ignore the element if it does not contain the id field
  • if it contains the virtual field set to true, create the port using the rest of supplied fields (if device supports virtual ports)
  • update modifiable attributes of the corresponding port using the rest of supplied fields

Any field in these dictionaries that is not used to create a virtual port and does not belong to a modifiable attribute, whether the respective attribute exists or not, must be silently ignored. However, any error in creating a port or applying a port's modifiable attribute must immediately stop the processing of request and must result in a corresponding error response.

The value field, if present, should be used to update the port value; any errors occurred during setting the value must however be silently ignored.

Event generation should be suspended during the processing of this request. Instead, a full-update event should be generated at the end of the request, to inform consumers about the changes.

Response has no body.

Particular status codes: the possible status codes in case of an error are, with few exceptions, the status codes of POST /ports combined with those of PATCH /ports/{id}. In case of error, the additional id field must be included to help identifying which element in supplied list generated the error.

Example:

Consumer's request:

PUT /ports HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y
Content-Type: application/json; charset=utf-8
Content-Length: n

[
    {
        "id": "gpio0",
        "display_name": "GPIO 0",
        "enabled": true,
        "pull_up": true,
        "debounce": false
    },
    {
        "id": "adc0",
        "display_name": "Analog-Digital Converter Input 0",
        "enabled": true,
        "sampling_interval": 1000
    },
    {
        "id": "vport0",
        "display_name": "Virtual Port 0",
        "enabled": true,
        "type": "number",
        "min": 0,
        "max": 100,
        "step": 1,
        "integer": true
    }
]

Device's response:

HTTP/1.1 204 No Content
Connection: close

DELETE /ports/{id}

Removes a virtual port from the device.

This is an optional API function and may not be available in all implementations. The consumer may use GET /device to find out whether this is supported or not by the device by inspecting the attribute virtual_ports.

This API function requires admin access level.

Path has the following arguments:

  • id specifies the identifier of the port to be removed

Request has no body.

Response has no body.

Particular status codes:

  • 400 Bad Request - error field values:
    • "port-not-removable" - the port is not a virtual port and thus cannot be removed
  • 404 Not Found - error field values:
    • "no-such-port" - a port with the given id does not exist

Example:

Consumer's request:

DELETE /ports/vport1 HTTP/1.1
Host: device.example.com
Connection: close
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y

Device's response:

HTTP/1.1 204 No Content
Connection: close

Port Values

GET /ports/{id}/value

Returns the current value of a port.

This API function requires at least viewonly access level.

Path has the following arguments:

  • id specifies the identifier of the desired port

Response has the following body:

value
  • value is the value read from the port; null should be returned if the value is unavailable or if the port is disabled

Particular status codes:

  • 404 Not Found - error field values:
    • "no-such-port" - a port with the given id does not exist
  • 502 Bad Gateway - error field values:
    • "port-error" - error communicating with the port (e.g. a hardware fault); additional message field may show details about the error
  • 504 Gateway Timeout - error field values:
    • "port-timeout" - communication with the port timed out

Example:

Consumer's request:

GET /ports/gpio0/value HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y

Device's response:

HTTP/1.1 200 OK
Connection: close
Cache-Control: no-cache
Content-Type: application/json; charset=utf-8
Content-Length: n

true

PATCH /ports/{id}/value

Sets the value of a port. The port's attributes enabled and writable must be true for this function to succeed. The value type must match the port type and will be written to the port as soon as possible; the request is responded after the writing process is complete, whether it was successful or erroneous.

This API function requires at least normal access level.

Path has the following arguments:

  • id specifies the identifier of the desired port

Request has the following body:

value
  • value supplied in the body will be written to the port

Response has no body.

Particular status codes:

  • 202 Accepted - the port value hasn't been applied right away (i.e. the port has been supplied the new value, but it still reads the old value)
  • 400 Bad Request - error field values:
    • "read-only-port" - this is a read-only port and cannot be written to
    • "invalid-value" - the value supplied is invalid (see Value Validation)
    • "port-disabled" - the port's attribute enabled is false
  • 404 Not Found - error field values:
    • "no-such-port" - a port with the given id does not exist
  • 502 Bad Gateway - error field values:
    • "port-error" - error communicating with the port (e.g. a hardware fault); additional message field may show details about the error
  • 504 Gateway Timeout - error field values:
    • "port-timeout" - communication with the port timed out

Example:

Consumer's request:

PATCH /ports/gpio0/value HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y
Content-Type: application/json; charset=utf-8
Content-Length: n

false

Device's response:

HTTP/1.1 204 No Content
Connection: close

PATCH /ports/{id}/sequence

This is an optional API function and may not be available in all implementations. The consumer may use GET /device to find out whether this is supported or not by the device by checking the presence of the "sequences" value in the flags device attribute.

Writes a sequence of values to a port, specifying the delay between each two successive values. The request is responded right away, before actually writing the sequence to the port. If the port has previously been configured to use a value expression, the request is rejected. If the port currently has a running sequence, the current sequence is stopped and replaced by the new one. Calling PATCH /ports/{id}/value during the execution of a sequence should have its normal effect without interrupting or canceling the sequence.

The port's attributes enabled and writable must be true for this function to succeed. When disabling a port (i.e. setting the enabled attribute to false) or when removing a virtual port, a current sequence for that port, if present, must immediately be cancelled.

This API function requires at least normal access level.

Path has the following arguments:

  • id specifies the identifier of the desired port

Request has the following body:

{
    "values": boolean[]|number[],
    "delays": number[],
    "repeat": number
}
  • values is the list of values to be written to the port. The device should be able to handle at least 256 sequence values. At least one value must be present in the sequence.
  • delays is the list of delays, in milliseconds, between values. It must have exactly the same number of elements as values. Each element must be a positive integer number between 1 and 60000. The n-th delay will be the pause between the n-th and the (n+1)-th value. The last value in the delays list is used as delay between two sequence iterations.
  • repeat is an integer number between 0 and 65535. Its value has the following meanings:
    • 0 will make the sequence repeat indefinitely
    • 1 will make the sequence run only once (the last element in the delays list will be ignored)
    • any greater number will make the sequence run a number of times equal to that number

A sequence can explicitly be cancelled by setting an expression, by disabling the port or by setting a new sequence. Setting an empty sequence is allowed and has the effect of cancelling a previous sequence. Devices may choose to persist repeating sequences, so that they can start over after a reset.

Response has no body.

Particular status codes:

  • 400 Bad Request - error field values:
    • "read-only-port" - this is a read-only port and cannot be written to
    • "port-with-expression" - this port currently has an expression
    • "invalid-field" - one of the fields is invalid; additional field field indicates the name of the field:
      • "values" - the values field is not a list or one of the values supplied is invalid (see Value Validation)
      • "delays" - the delays field is not a list, one of the delays supplied is invalid or the list lengths don't match
      • "repeat" - the repeat field is not an integer number in the range 0 - 65535
    • "port-disabled" - the port's attribute enabled is false
  • 404 Not Found - error field values:
    • "no-such-port" - a port with the given id does not exist

Example:

Consumer's request:

PATCH /ports/gpio0/sequence HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y
Content-Type: application/json; charset=utf-8
Content-Length: n

{
    "values": [true, false, true, false],
    "delays": [1000, 1000, 1000, 3000],
    "repeat": 5
}

Device's response:

HTTP/1.1 204 No Content
Connection: close

History

GET /ports/{id}/history

Returns a slice of the value history of a port. For more details, see the History mechanism.

There are two ways in which this API endpoint can be used, depending on the supplied query arguments: obtaining a slice of records between two moments in time and obtaining a list of records corresponding to a list of supplied moments.

To retrieve a slice of records, the query arguments from, to and limit are used to determine the interval of time and (maximum) amount of records to be returned. All records with timestamps between from (inclusively) and to (exclusively) must be considered, but no more than limit records (starting at from) must be returned. The records must be sorted by timestamp, in ascending order.

To retrieve the records corresponding to a list of given moments, the query argument timestamps is used to supply the list of moments. For each moment in the list, the closest record having a sampling timestamp equal or less than the given moment must be returned. The order of the returned list must correspond to the order of the input timestamps. If no record matching the timestamp condition can be found, a null element must be returned for that timestamp.

This is an optional API function and may not be available in all implementations. The consumer may use GET /device to find out whether this is supported or not by the device by checking the presence of the "history" value in the flags device attribute.

The device must never return values that were unavailable at the moment of sampling (i.e. null values).

This API function requires at least viewonly access level.

Path has the following arguments:

  • id specifies the identifier of the desired port

Request has the following query arguments:

  • from is an optional, positive number indicating the start of the interval, given as number of milliseconds since Epoch.
  • to is an optional, positive number indicating the stop of the interval, given as number of milliseconds since Epoch; if not supplied, the current timestamp must be assumed.
  • limit is an optional, positive number indicating the maximum number of records to be returned; valid values range from 1 to 10000; if not supplied, the default value of 1000 must be used.
  • timestamps is an optional, comma-separated list of timestamps, given as numbers of milliseconds since Epoch (e.g. 1605381337123,1605381347123,1605381357123). At least 100 values must be accepted.

If both timestamps and another query argument (indicating a slice) are supplied, timestamps takes precedence. At least one of timestamp and from arguments must be specified.

Response has the following body:

[
    {
        "timestamp": number,
        "value": boolean|number
    },
    ...
]
  • timestamp is the sampling timestamp, in milliseconds since Epoch
  • value is the sampled port value

Particular status codes:

  • 400 Bad Request - error field values:
    • "invalid-field" - one of the fields is invalid; additional field field indicates the name of the field:
      • "from" - from is not a positive integer
      • "to" - to is not a positive integer
      • "limit" - limit is not an integer between 1 and 10000
      • "timestamps" - timestamps is not a list of comma-separated positive integers
  • 404 Not Found - error field values:
    • "no-such-port" - a port with the given id does not exist

Example:

Consumer's request:

GET /ports/adc0/history?from=1605381337000&to=1605381340000 HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y

Device's response:

HTTP/1.1 200 OK
Connection: close
Cache-Control: no-cache
Content-Type: application/json; charset=utf-8
Content-Length: n

[
    {
        "timestamp": 1605381337123,
        "value": 523
    },
    {
        "timestamp": 1605381338234,
        "value": 533
    },
    {
        "timestamp": 1605381339321,
        "value": 445
    }
]

DELETE /ports/{id}/history

Removes history data of a port between two given timestamps. For more details, see the History mechanism.

The device must permanently remove history records of the port whose timestamps are greater than or equal to from and less than to.

This is an optional API function and may not be available in all implementations. The consumer may use GET /device to find out whether this is supported or not by the device by checking the presence of the "history" value in the flags device attribute.

This API function requires admin access level.

Path has the following arguments:

  • id specifies the identifier of the port to be removed

Request has the following query arguments:

  • from is a positive number indicating the start of the interval, given as number of milliseconds since Epoch.
  • to is a positive number indicating the stop of the interval, given as number of milliseconds since Epoch.

Response has no body.

Particular status codes:

  • 400 Bad Request - error field values:
    • "invalid-field" - one of the fields is invalid; additional field field indicates the name of the field:
      • "from" - from is not a positive integer
      • "to" - to is not a positive integer
  • 404 Not Found - error field values:
    • "no-such-port" - a port with the given id does not exist

Example:

Consumer's request:

DELETE /ports/adc0/history?from=1605381337000&to=1605467737000 HTTP/1.1
Host: device.example.com
Connection: close
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y

Device's response:

HTTP/1.1 204 No Content
Connection: close

Notifications

GET /webhooks

Returns the current webhooks settings.

This is an optional API function and may not be available in all implementations. The consumer may use GET /device to find out whether this is supported or not by the device by checking the presence of the "webhooks" value in the flags device attribute.

This API function requires admin access level.

Response has the following body:

{
    "enabled": boolean,
    "scheme": string,
    "host": string,
    "port": number,
    "path": string,
    "password_hash": string,
    "events": string,
    "timeout": number,
    "retries": number
}
  • enabled tells whether webhooks notifications are enabled or not
  • password_hash is the lowercase sha256 hex digest of the webhooks password

For the rest of the fields, see Webhooks.

Example:

Consumer's request:

GET /webhooks HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y

Device's response:

HTTP/1.1 200 OK
Connection: close
Cache-Control: no-cache
Content-Type: application/json; charset=utf-8
Content-Length: n

{
    "enabled": "true",
    "scheme": "http",
    "host": "consumer.example.com",
    "port": 80,
    "path": "/device1/notify/",
    "password_hash": "6cad63595165fc39c9a3c715b874d406758bccb91e98106db8cd21da3150f658",
    "events": ["value-change", "port-add"],
    "timeout": 60,
    "retries": 3
}

PUT /webhooks

Configures the webhooks settings.

This is an optional API function and may not be available in all implementations. The consumer may use GET /device to find out whether this is supported or not by the device by checking the presence of the "webhooks" value in the flags device attribute.

This API function requires admin access level.

Request has the following body:

{
    "enabled": boolean,
    "scheme": string,
    "host": string,
    "port": number,
    "path": string,
    "password": string,
    "password_hash": string,
    "events": string,
    "timeout": number,
    "retries": number
}
  • enabled indicates whether webhooks notifications are enabled or not
  • password_hash is the lowercase SHA256 hex digest of the webhooks password

All fields are mandatory; however, only one of password and password_hash must be supplied. The fields host and path may be empty, but only when enabled is false. See Webhooks for details and meaning of each of the field.

Response has no body.

Particular status codes:

  • 400 Bad Request - error field values:
    • "invalid-field" - one of the fields is invalid; additional field field indicates the name of the field:
      • "scheme" - the given scheme is not supported
      • "host" - the given host is non-empty and is not a valid hostname, domain name or IP address, or is empty and enabled is true
      • "port" - the given port is not a number between 1 and 65535
      • "path" - the given path is empty and enabled is true
      • "password" - the given password is not a valid password of at most 32 characters
      • "password_hash" - the given password_hash is not a valid SHA256 hex digest
      • "events" - the events field is not a list or contains an unknown event type
      • "timeout" - the given timeout is not a number between 1 and 3600
      • "retries" - the given retires field is not a number between 0 and 10

Example:

Consumer's request:

PUT /webhooks HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y
Content-Type: application/json; charset=utf-8
Content-Length: n

{
    "enabled": "true",
    "scheme": "http",
    "host": "consumer.example.com",
    "port": 80,
    "path": "/device1/notify/",
    "password": "deadbeef",
    "events": ["value-change", "port-add"],
    "timeout": 60,
    "retries": 3
}

Device's response:

HTTP/1.1 204 No Content
Connection: close

GET /listen

Waits for events to occur on the device. The request is responded as soon as the first event occurs. If there are pending events for the requesting consumer's session, they are served right away. For more details, see the Listening notification mechanism.

This is an optional API function and may not be available in all implementations. The consumer may use GET /device to find out whether this is supported or not by the device by checking the presence of the "listen" flag in the flags device attribute.

This API function requires at least viewonly access level.

When the number of maximum supported parallel listen requests is reached and a listen request with for a new session is received, the device must immediately respond to an existing listen request with a response with no events and thus free a session.

When a request with a Session-Id header is received while another pending listen request with the same Session-Id header exists, the latter must be immediately responded with no events.

Request has the following query arguments:

  • timeout optional, specifies a keep-alive timeout, in seconds, after which an empty list response is returned if no event occurred. The default value is 60. If given, its value must be between 1 and 3600.

Response has the following body:

[
    {
        "type": string,
        "params": any
    },
    ...
]

Each dictionary in the returned list represents an event. Usually only one event is returned, right after it occurs. However if there are pending events that occurred between two listen requests, all of them are returned with one response, in chronological order. The response after a timeout is an empty list.

  • type is the type of the event, as defined in Events.
  • params are the parameters, as defined in Events.

The event dictionary may contain other implementation-specific fields.

The response body may contain JSON References.

Particular status codes:

  • 400 Bad Request - error field values:
    • "invalid-field" - one of the fields is invalid; additional field field indicates the name of the field:
      • "timeout" - timeout is not an integer between 1 and 3600

Example:

Consumer's request:

GET /listen?timeout=120 HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y
Session-Id: webconsumer-f49cf638

Device's response:

HTTP/1.1 200 OK
Connection: close
Cache-Control: no-cache
Content-Type: application/json; charset=utf-8
Content-Length: n

[
    {
        "type": "value-change",
        "params": {
            "id": "gpio0",
            "value": true,
            "old_value": false
        }
    }
]

Reverse API Calls

GET /reverse

Returns the current reverse API calls configuration.

This is an optional API function and may not be available in all implementations. The consumer may use GET /device to find out whether this is supported or not by the device by checking the presence of the "reverse" value in the flags device attribute.

This API function requires admin access level.

Response has the following body:

{
    "enabled": boolean,
    "scheme": string,
    "host": string,
    "port": number,
    "path": string,
    "password_hash": string,
    "device_id": string,
    "timeout": number
}
  • enabled tells whether reverse API calls are enabled or not.
  • password_hash is the lowercase sha256 hex digest of the webhooks password.

For the rest of the fields, see the The Reverse API Calls Mechanism.

Example:

Consumer's request:

GET /reverse HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y

Device's response:

HTTP/1.1 200 OK
Connection: close
Cache-Control: no-cache
Content-Type: application/json; charset=utf-8
Content-Length: n

{
    "enabled": "true",
    "scheme": "http",
    "host": "consumer.example.com",
    "port": 80,
    "path": "/device1/reverse/",
    "password_hash": "6cad63595165fc39c9a3c715b874d406758bccb91e98106db8cd21da3150f658",
    "device_id": "621fa3fb"
    "timeout": 60
}

PUT /reverse

Configures the reverse API call mechanism.

This is an optional API function and may not be available in all implementations. The consumer may use GET /device to find out whether this is supported or not by the device by checking the presence of the "reverse" value in the flags device attribute.

This API function requires admin access level.

Request has the following body:

{
    "enabled": boolean,
    "scheme": string,
    "host": string,
    "port": number,
    "path": string,
    "password": string,
    "password_hash": string,
    "device_id": string,
    "timeout": number
}
  • enabled indicates whether the mechanism is enabled or not
  • password_hash is the lowercase SHA256 hex digest of the password

All fields are mandatory; however, only one of password and password_hash must be supplied. The fields host, path and device_id may be empty, but only when enabled is false. See The Reverse API Calls Mechanism for details and meaning of each field.

Response has no body.

Particular status codes:

  • 400 Bad Request - error field values:
    • "invalid-field" - one of the fields is invalid; additional field field indicates the name of the field:
      • "scheme" - the given scheme is not supported
      • "host" - the given host is non-empty and is not a valid hostname, domain name or IP address, or is empty and enabled is true
      • "port" - the given port is not a number between 1 and 65535
      • "path" - the given path is empty and enabled is true
      • "password" - the given password is not a valid password of at most 32 characters
      • "password_hash" - the given password_hash is not a valid SHA256 hex digest
      • "device_id" - the given device_id field does not follow the field name restrictions, or is empty and enabled is true
      • "timeout" - the given timeout is not a number between 1 and 3600

Example:

Consumer's request:

PUT /reverse HTTP/1.1
Host: device.example.com
Connection: close
Cache-Control: no-cache
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c3IiOiJhZG1pbiIsImlhdCI6MTUxNjIzOTAyMiwib3JpIjoiY29uc3VtZXIiLCJpc3MiOiJxVG9nZ2xlIn0.-Ckcvq_Gk6RsW3pa-diWWiqQ4za0MCNGCXvdio1MK5Y
Content-Type: application/json; charset=utf-8
Content-Length: n

{
    "enabled": "true",
    "scheme": "http",
    "host": "consumer.example.com",
    "port": 80,
    "path": "/device1/reverse/",
    "device_id": "621fa3fb",
    "password": "deadbeef",
    "timeout": 60
}

Device's response:

HTTP/1.1 204 No Content
Connection: close
Clone this wiki locally