-
Notifications
You must be signed in to change notification settings - Fork 2
The qToggle API 1.1
- Abstract
- Versions
- Definitions & Terminology
- Standard Device Attributes
- Standard Port Attributes
- Expressions
- Messages
- The HTTP Protocol
- Authentication
- Notifications
- The Reverse API Calls Mechanism
- Backup & Restore
- History
- API Functions
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.
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.
A qToggle-enabled device is any machine that implements the mandatory API functions described by this document.
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.
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.
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.
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.
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.
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:
- native/hardware (e.g. interrupts or hardware register changes)
- API calls to
PATCH /ports/{id}/value
- sequences, as a result to
PATCH /ports/{id}/sequence
API calls - value expression evaluation, for ports configured with Expressions
The device should be prepared to handle port values that are unavailable (cases when no value is known for a certain port).
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:
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 typestring
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 optionalstring
field that can be used by the consumer to show the attribute in a user interface. The maximum length of this field's value is64
. -
description
field of typestring
is a human-readable description of the attribute. The maximum length of this field's value is64
. -
type
field of typestring
indicates the data type of the attribute and is one of:"boolean"
,"number"
,"string"
. -
modifiable
field of typeboolean
tells whether the attribute can be modified or not. -
unit
is an optionalstring
unit of measurement for the attribute (e.g."seconds"
). The maximum length of this field's value is16
. -
min
andmax
are optionalnumber
fields used by attributes of typenumber
andstring
to represent the range of accepted values, or the minimum and maximum length of the string, respectively. -
integer
is an optionalboolean
field specific to attributes of typenumber
and, if present, tells whether the attribute value must be an integer or it can be any real number. -
step
is an optionalnumber
field specific to attributes of typenumber
and, if present, restricts valid values to those equal tomin
plus a multiple ofstep
; this field only makes sense whenmin
is defined. -
choices
is an optional list of dictionaries, valid only for attributes of typesnumber
orstring
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'stype
(number
orstring
) and represents the choice value. -
display_name
is an optional field of typestring
that can be used by consumers to display the choice in a user interface.
-
-
reconnect
is an optionalboolean
field, valid only for modifiable attributes, indicating that the consumer should expect the device to disconnect and reconnect shortly after updating this attribute.
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"
This section defines all the standard device attributes. For more details see Attributes.
These are attributes that must be exposed by all devices.
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.
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
.
This attribute is a string representing the firmware and/or hardware version of the device.
This string
attribute is not modifiable.
This attribute represents the version of the implemented qToggle API.
This string
attribute is not modifiable.
This attribute indicates vendor of this qToggle API implementation. The recommended format is "{organization}/{implementation_name}"
.
This string
attribute is not modifiable.
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
.
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.
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
.
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
.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
.
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
.
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
.
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.
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.
This attribute could be exposed by devices to indicate their (core) temperature, in degrees celsius.
This number
attribute is not modifiable.
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.
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.
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.
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.
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.
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.
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.
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.
This section defines all the standard port attributes. For more details see Attributes.
These are attributes that must be exposed by all ports.
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
.
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
.
This attribute is the type of the port, as described in Port Types.
This string
attribute is not modifiable.
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.
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.
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
.
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
.
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 typenumber
and represents the choice value. -
display_name
is an optional field of typestring
that can be used by consumers to display the choice in a user interface.
This attribute is not modifiable.
This attribute may be exposed by number
ports to indicate a minimum accepted value.
This number
attribute is not modifiable.
This attribute may be exposed by number
ports to indicate a maximum accepted value.
This number
attribute is not modifiable.
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.
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.
This attribute must be exposed by Virtual Ports and its value must be true
.
This boolean
attribute is not modifiable.
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.
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.
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.
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
.
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
.
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
.
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
.
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
.
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.
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.
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:
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
.
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
.
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
.
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
.
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"
}
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"
}
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
.
This reason should be used when the given expression is empty (or contains only whitespace). Field details
has the following format:
"details": {
"reason": "empty"
}
This reason should be used when the given expression length exceeds maximum supported length. Field details
has the following format:
"details": {
"reason": "too-long"
}
Expressions are evaluated upon specific triggers and the resulted value is then used to decide a port's read or written value.
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.
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.
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 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.
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.
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.
Result of a port reference must be unavailable, unless passed an argument to a function.
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.
-
ADD(v1, v2, ...)
Takes at least two arguments and returns the sum of all arguments.
-
SUB(v1, v2)
Returns the difference between
v1
andv2
. -
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
byv2
. -
MOD(v1, v2)
Returns the modulo of the division of
v1
byv2
. -
POW(b, e)
Returns
b
to the powere
.
-
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
andv2
.
-
BITAND(v1, v2)
Performs the bitwise and operation between
v1
andv2
. -
BITOR(v1, v2)
Performs the bitwise or operation between
v1
andv2
. -
BITNOT(v)
Returns the bitwise complement of
v
. -
BITXOR(v1, v2)
Performs the bitwise exclusive or operation between
v1
andv2
. -
SHL(v, k)
Performs the bitwise shift left operation on
v
withk
bits. -
SHR(v, k)
Performs the bitwise shift right operation on
v
withk
bits.
-
IF(c, t, f)
If
c
istrue
, returnst
; otherwise returnsf
. -
EQ(v1, v2)
Returns
true
ifv1
is equal tov2
; otherwise returnsfalse
. -
GT(v1, v2)
Returns
true
ifv1
is greater thanv2
; otherwise returnsfalse
. -
GTE(v1, v2)
Returns
true
ifv1
is greater than or equal tov2
; otherwise returnsfalse
. -
LT(v1, v2)
Returns
true
ifv1
is less thanv2
; otherwise returnsfalse
. -
LTE(v1, v2)
Returns
true
ifv1
is less than or equal tov2
; otherwise returnsfalse
.
-
ABS(v)
Returns the absolute value of
v
. -
SGN(v)
Returns
1
ifv
is strictly positive,-1
ifv
is negative and0
if it's0
.
-
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).
-
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 tod
digits after the decimal point.
-
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.
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
). Ift
is not supplied, returns the current year. The local real date/time should be considered instead ofUTC
, 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
to12
). Ift
is not supplied, returns the current month. The local real date/time should be considered instead ofUTC
, 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
to31
). Ift
is not supplied, returns the current day. The local real date/time should be considered instead ofUTC
, 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 is6
). Ift
is not supplied, returns the current day of week. The local real date/time should be considered instead ofUTC
, 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
to31
). Ift
is not supplied, returns the last day of the current month. The local real date/time should be considered instead ofUTC
, 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
to23
). Ift
is not supplied, returns the current hour. The local real date/time should be considered instead ofUTC
, 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
to59
). Ift
is not supplied, returns the current minute. The local real date/time should be considered instead ofUTC
, 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
to59
). Ift
is not supplied, returns the current second. The local real date/time should be considered instead ofUTC
, 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
to999
).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
to1439
). Ift
is not supplied, returns the current minute of the day. The local real date/time should be considered instead ofUTC
, 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
to86399
). Ift
is not supplied, returns the current second of the day. The local real date/time should be considered instead ofUTC
, 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 whichn
is0
). Negativen
values go back in time. Ifn
is not supplied, it must be considered0
(i.e. current year). The local real date/time should be considered instead ofUTC
, 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 whichn
is0
). Negativen
values go back in time. Implementations must allow going beyond current year, both in the past and future. Ifn
is not supplied, it must be considered0
(i.e. current month). The local real date/time should be considered instead ofUTC
, if available.This function should be evaluated once every second.
-
BOW(n, s)
Takes two optional arguments
n
ands
and returns the beginning of then
-th week, as Unix timestamp, in seconds, relative to the current week (for whichn
is0
). Negativen
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 is0
(default if not supplied). Ifn
is not supplied, it must be considered0
(i.e. current week). The local real date/time should be considered instead ofUTC
, if available.This function should be evaluated once every second.
-
BOD(n)
Takes an optional argument
n
and returns the beginning of then
-th day, as Unix timestamp, in seconds, relative to the current day (for whichn
is0
). Negativen
values go back in time. Implementations must allow going beyond current year, both in the past and future. Ifn
is not supplied, it must be considered0
(i.e. current day). The local real date/time should be considered instead ofUTC
, 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), andfalse
otherwise. The local real date/time should be considered instead ofUTC
, 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), andfalse
otherwise. The local real date/time should be considered instead ofUTC
, if available.This function should be evaluated once every second.
-
DELAY(v, t)
Returns the value of
v
delayed byt
milliseconds. The history buffer size is left to the implementation. The initial value of the function isv
.This function should be evaluated as often as possible.
-
SAMPLE(v, t)
Evaluates value
v
eacht
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
fort
milliseconds. Returns the last value ofv
that wasn't ignored. Ignoring timer starts wheneverv
changes and timer is off. Timet
is evaluated only when timer starts. Initial value isv
and timer is initially on.This function should be evaluated as often as possible while timer is active.
-
HELD(v, k, t)
Returns
true
ifv
is held to the valuek
for at leastt
milliseconds. The result then becomesfalse
with the first change of valuev
.This function should be evaluated as often as possible while
v
is equal tok
for less thant
milliseconds. -
DERIV(v, t)
Computes the discrete derivative of
v
. It returns the difference quotient from the last knownv
value by dividing the difference between current and lastv
value by the amount of time (in seconds, represented as floating point, with reasonably high precision) that has passed since the lastv
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) is0
. -
INTEG(v, a, t)
Computes the discrete integral of
v
, usinga
as previous accumulator value. It multiplies the arithmetic mean of currentv
and lastv
by the amount of time (in seconds, represented as floating point, with reasonably high precision) that has passed since the lastv
value evaluation, adds it toa
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) isa
. -
FMAVG(v, n, t)
Applies a moving average filter on the last
n
values ofv
. It returns the arithmetic mean of the most recentn
values ofv
.The function must be evaluated as often as indicated by the sampling interval
t
, in milliseconds.Minimum
n
value is1
, 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 ofv
. It returns the median value of the most recentn
values ofv
.The function must be evaluated as often as indicated by the sampling interval
t
, in milliseconds.Minimum
n
value is1
, while maximum is left to the implementation.If less than
n
values have been gathered so far, filtering is applied on all values.
-
AVAILABLE(v)
Returns
true
ifv
is an available value andfalse
otherwise. -
DEFAULT(v, d)
Returns
v
ifv
is available andd
otherwise. Equivalent toIF(AVAILABLE(v), v, d)
. -
RISING(v)
Returns
true
if valuev
is strictly greater than the last value, orfalse
otherwise.Initial result (when the last
v
value is not known) isfalse
. -
FALLING(v)
Returns
true
if valuev
is strictly lower than the last value, orfalse
otherwise.Initial result (when the last
v
value is not known) isfalse
. -
ACC(v, a)
Accumulates changes in
v
, usinga
as previous accumulator value. Calculates the difference from currentv
and last evaluated value forv
. Adds the difference toa
and returns the value.Initial result (when the last
v
value is not known) isa
. -
ACCINC(v, a)
Identical to
ACC
but considers only increases of valuev
and discards decreases. -
HYST(v, t1, t2)
Implements a hysteresis around value
v
; returned value is as follows:- if previously returned result was
false
, returnsfalse
if valuev
is below or equal to thresholdt2
, ortrue
otherwise - if previously returned result was
true
, returnstrue
if valuev
is above or equal to thresholdt1
, orfalse
otherwise
Previously returned value is initially considered
false
. - if previously returned result was
-
ONOFFAUTO(v, a)
Helps building on/off/auto selection buttons, returning:
-
true
ifv
is strictly greater than0
-
false
ifv
is strictly less than0
-
a
ifv
is equal to0
-
-
SEQUENCE(v1, d1, ...)
Returns the current value in a sequence of values
v1
,v2
, ... with delaysd1
,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 thePATCH /ports/{id}/sequence
API call whenrepeat
is set to0
(i.e. repeat indefinitely).This function must only be implemented by devices that support sequences (i.e. those that expose
"sequences"
in theflags
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 amongx1
,x2
, ... and returns the correspondingyi
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 whosexi
values are the closest tox
and are lower than or equal to and, respectively, higher than or equal tox
. Returns they
value obtained by linear interpolation ofx
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 timestampt
, in seconds. Given that it's often unlikely for a sampled value to be found at exact given timestamp, the closest value tot
must be returned, according to the argumentd
. Considering the differencediff(s)
between sampling time of samples
andt
:- if
d
is strictly positive, the sample for whichdiff(s)
is positive or zero, minimal and less thand
must be considered, if such a sample exists - if
d
is strictly negative, the sample for whichdiff(s)
is negative or zero, maximal and greater thand
must be considered, if such a sample exists - if
d
is0
, the sample for whichdiff(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 theflags
attribute). - if
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 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.
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.
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.
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 themin
value - if a
max
restriction is defined and value data type is number, the value must be less than or equal to themax
value - if a
min
restriction is defined and value data type is string, the value length must be greater than or equal to themin
value - if a
max
restriction is defined and value data type is string, the value length must be less than or equal to themax
value - if the
integer
restriction is defined andtrue
, the value must be an integer number - if the
step
restriction is defined, the value must be equal tomin
plus a multiple ofstep
- if the
choices
restriction is defined, the value must be equal to one of the choice values in thechoices
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.
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.
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; additionalfield
field indicates the name of the missing field -
"invalid-field"
- the value of the indicated field is invalid; additionalfield
field indicates the name of the invalid field -
"missing-header"
- a required header is missing from the request; additionalheader
field indicates the name of the missing header -
"invalid-header"
- the value of the indicated header is invalid; additionalheader
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 additionalrequired_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 additionalmessage
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.
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.
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.
With API functions that use the GET
method, function arguments (if any) must transmitted as an URL-encoded query, as they have no body.
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
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"
.
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"
).
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
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.
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.
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.
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.
A device response to a normal API call is an HTTP response that should not be authenticated.
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.
A consumer response to a webhooks call is an HTTP response that should not be authenticated.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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 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:
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 from1
to3600
. -
retries
is the number of retries in case of failure. Valid values range from0
to10
.
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.
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.
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 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"
.
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 from1
to3600
.
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.
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.
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.
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.
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.
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
.
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
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:
GET /device
GET /ports
-
GET /webhooks
(where the device supports webhooks) -
GET /reverse
(where the device supports reverse API calls)
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:
- Issue a
GET /backup/endpoints
request to find the extra backup endpoints, if device exposes"backup"
flag. - Issue separate
GET
requests to the standard endpoints above. - Issue separate
GET
requests to all extra backup endpoints. - 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.
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:
-
Consider the following standard, backup-specific endpoints:
-
PUT /device
withorder
10 -
PUT /ports
withorder
20 -
PUT /webhooks
withorder
30 -
PUT /reverse
withorder
40
-
-
Issue a
GET /backup/endpoints
request to find the extra backup endpoints. -
Sort all gathered endpoints by their order, ascending.
-
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.
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.
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.
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
}
}
}
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
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; additionalattribute
field indicates the name of the attribute -
"no-such-attribute"
- the attribute does not exist; additionalattribute
field indicates the name of the attribute -
"invalid-field"
- the value supplied for one of the attributes is invalid (see Value Validation); additionalfield
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
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 totrue
, 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
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"
}
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 aGET /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
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 ofadmin
,normal
,viewonly
andnone
.
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"
}
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
orPUT
-
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
}
]
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": {}
}
]
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; additionalattribute
field indicates the name of the attribute -
"no-such-attribute"
- the attribute does not exist; additionalattribute
field indicates the name of the attribute -
"invalid-field"
- the value supplied for one of the attributes is invalid (see Value Validation); additionalfield
field indicates the name of the attribute
-
-
404 Not Found
-error
field values:-
"no-such-port"
- a port with the givenid
does not exist
-
-
502 Bad Gateway
-error
field values:-
"port-error"
- error communicating with the port (e.g. a hardware fault); additionalmessage
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
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
andchoices
are optional, accepted only fornumber
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; additionalfield
field indicates the name of the field:-
"id"
- the givenid
is invalid -
"type"
- the giventype
is not one of the supported types -
"min"
- the givenmin
is not a number -
"max"
- the givenmax
is not a number of is less than givenmin
-
"step"
- the givenstep
is not a number -
"choices"
- the givenchoices
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": {}
}
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 totrue
, 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
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 givenid
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
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 givenid
does not exist
-
-
502 Bad Gateway
-error
field values:-
"port-error"
- error communicating with the port (e.g. a hardware fault); additionalmessage
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
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 attributeenabled
isfalse
-
-
404 Not Found
-error
field values:-
"no-such-port"
- a port with the givenid
does not exist
-
-
502 Bad Gateway
-error
field values:-
"port-error"
- error communicating with the port (e.g. a hardware fault); additionalmessage
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
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 least256
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 asvalues
. Each element must be a positive integer number between1
and60000
. The n-th delay will be the pause between the n-th and the (n+1)-th value. The last value in thedelays
list is used as delay between two sequence iterations. -
repeat
is an integer number between0
and65535
. 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 thedelays
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; additionalfield
field indicates the name of the field:-
"values"
- thevalues
field is not a list or one of the values supplied is invalid (see Value Validation) -
"delays"
- thedelays
field is not a list, one of the delays supplied is invalid or the list lengths don't match -
"repeat"
- therepeat
field is not an integer number in the range0
-65535
-
-
"port-disabled"
- the port's attributeenabled
isfalse
-
-
404 Not Found
-error
field values:-
"no-such-port"
- a port with the givenid
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
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 from1
to10000
; if not supplied, the default value of1000
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 least100
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; additionalfield
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 between1
and10000
-
"timestamps"
-timestamps
is not a list of comma-separated positive integers
-
-
-
404 Not Found
-error
field values:-
"no-such-port"
- a port with the givenid
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
}
]
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; additionalfield
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 givenid
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
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
}
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; additionalfield
field indicates the name of the field:-
"scheme"
- the givenscheme
is not supported -
"host"
- the givenhost
is non-empty and is not a valid hostname, domain name or IP address, or is empty andenabled
istrue
-
"port"
- the givenport
is not a number between1
and65535
-
"path"
- the givenpath
is empty andenabled
istrue
-
"password"
- the givenpassword
is not a valid password of at most32
characters -
"password_hash"
- the givenpassword_hash
is not a valid SHA256 hex digest -
"events"
- theevents
field is not a list or contains an unknown event type -
"timeout"
- the giventimeout
is not a number between1
and3600
-
"retries"
- the givenretires
field is not a number between0
and10
-
-
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
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 is60
. If given, its value must be between1
and3600
.
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; additionalfield
field indicates the name of the field:-
"timeout"
-timeout
is not an integer between1
and3600
-
-
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
}
}
]
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
}
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; additionalfield
field indicates the name of the field:-
"scheme"
- the givenscheme
is not supported -
"host"
- the givenhost
is non-empty and is not a valid hostname, domain name or IP address, or is empty andenabled
istrue
-
"port"
- the givenport
is not a number between1
and65535
-
"path"
- the givenpath
is empty andenabled
istrue
-
"password"
- the givenpassword
is not a valid password of at most32
characters -
"password_hash"
- the givenpassword_hash
is not a valid SHA256 hex digest -
"device_id"
- the givendevice_id
field does not follow the field name restrictions, or is empty andenabled
istrue
-
"timeout"
- the giventimeout
is not a number between1
and3600
-
-
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