CARVIEW |
In the context of the Web of Things (WoT), a Binding is a blueprint that gives guidance on how to implement a specific IoT protocol, data format, or IoT platform. The WoT Thing Description specification explains the overall mechanism and the WoT Binding Registry provides the formal requirements for any binding to follow. This document is a binding for BACnet [[BACnet]], a building automation and control networking protocol.
More specifically, this document defines a set of vocabulary terms that can be used inside a Thing Description document, and associated rules that allow to describe WoT operations using BACnet over the network. Additionally, relevant examples are provided to showcase different vocabulary terms and the associated behavior.
This document is a work in progress
BACnet® is a registered trademark of ASHRAE. This document has not been reviewed for publication by ASHRAE, and no endorsement by ASHRAE is implied.
Introduction
BACnet [[BACnet]] is a building automation and control networking protocol for applications such as heating, ventilating, and air conditioning control, lighting control, access control, and fire detection systems.
This document describes how the Web of Things Thing Description [[WOT-THING-DESCRIPTION]] (TD) can be used to represent devices that use BACnet. In particular, the document explains how to create valid TD Forms for the different operations that BACnet can perform.
Forms of a Thing Description instance with BACnet Binding complies with this specification if it follows the normative statements in [[[#vocabulary]]] and [[[#mappings]]].
A JSON Schema [[?JSON-SCHEMA]] to validate Thing Description instances containing BACnet Binding is provided in the GitHub repository.
Binding Scope
It is assumed the reader of this document is familiar with the basic principles and mechanisms of the BACnet standard [[BACnet]].
The following subset of BACnet features is supported by this binding:
- Services: ReadProperty, WriteProperty, SubscribeCOV
- Data Model: Device, Object, Property, Index
This protocol binding is intended to be used to enable web applications to interact with data and control affordances exposed by BACnet devices. It is not intended that this binding be used for device commissioning, onboarding, or device management.
WoT Interaction Affordances are mapped to BACnet Properties. If the BACnet property-identifier is not present in the URI element of the form, the object-identifier is used to interact with the BACnet Object, exposing the present-value Property.
When it is necessary to expose properties other than the present-value property, the property-identifier must be included in the URI element of the form.
Selected BACnet Service parameters are exposed as URI Variables to enable WoT consumers access to BACnet features, for example, Command Priority.
URI Scheme
In cases where a URI is needed to refer to data that is accessible using BACnet services, the 'bacnet' URI scheme provides a means to identify a property of an object in the scope of a BACnet device. The 'bacnet' URI scheme does not provide means to identify the BACnet network on which a device resides, or the network access method, physical media, or protocol service to use.
The 'bacnet' URI Scheme used herein is based on the definition in Annex Q.8 of the BACnet Specification [[BACnet]]. The additions to Q.8 are as follows:
- object-type and property-identifier are restricted to their numeric versions and not the registered alphanumeric identifiers, in order to reduce the maintenance overhead when new object-types and property-identifiers are introduced in the BACnet standard.
- Query components (as per RFC 3986 Section 3.4) are added to cover specific needs not covered in Annex Q.8. Current examples are command priority and COV increment.
URI Syntax
The syntax for the 'bacnet' URI scheme is specified below in Augmented Backus-Naur Form (ABNF) [[RFC5234]]:
bacnet-URI = scheme ":" scheme-specific-part [uri-variable-part] scheme = "bacnet" scheme-specific-part = "//" device-identifier "/" object-identifier [ "/" property-identifier [ "/" property-array-index ] ] device-identifier = number / ".this" object-identifier = object-type "," object-instance object-type = number / identifier object-instance = number property-identifier = number / identifier property-array-index = number uri-variable-part = ?(key1=value1) *( "&" keyN=valueN ) number = "0" / non-zero-digit *decimal-digit non-zero-digit = %x31-39 ; "1" to "9" decimal-digit = %x30-39 ; "0" to "9" identifier = lowercase *alphanumeric *( "-" 1*alphanumeric ) alphanumeric = uppercase / lowercase / decimal-digit uppercase = %x41-5A ; "A" to "Z" lowercase = %x61-7A ; "a" to "z"
device-identifier
Component
The device
component specifies the device instance number in decimal. A
device
identifier of .this
means "this device" so that it can be used in static
files that do not need to be changed when the device identifier changes.
device-identifier = number / ".this"
A Protocol Binding conforming to this specification SHALL use number type for device-identifier.
object-identifier
Component
The object-identifier
component consist of two sub-components: the object-type and the
object-instance number. The object-type is either a decimal number in the range 0 to 210-1
(inclusive) or exactly equal to the identifier text of one of the named items of the BACnetObjectType
enumeration defined in Clause 21 of [[BACnet]]. The object-instance number is a decimal number in the range
0 to 222-1 (inclusive).
object-identifier = object-type "," object-instance object-type = number / identifier object-instance = number
A Protocol Binding conforming to this specification SHALL use number type for object-type.
property-identifier
Component
The property-identifier
component is either a decimal number or exactly equal to the identifier
text of one of the named items of the BACnetPropertyIdentifier enumeration defined in Clause 21 of
[[BACnet]]. If it is omitted, it defaults to present-value
except for BACnet File objects,
where the absence of the property
component refers to the entire content of the file accessed with Stream Access.
property-identifier = number / identifier
A Protocol Binding conforming to this specification SHALL use number type for property-identifier.
property-array-index
Component
The components of an array property may be individually accessed (read or written) using an "array index".
The index
component is a decimal number.
property-array-index = number
An index of 0 (zero) identifies the count of the number of data elements. If the array index is omitted, it means that all the elements of the array are to be accessed. An array index N, greater than zero, identifies the Nth element in the sequence.
uri-variable-part
Component
URI variables can be used to supply additional parameters to the BACnet operation. See [[[#vocabulary-uri-variables]]] for further details.
uri-variable-part = ?(key1=value1) *( "&" keyN=valueN )
A string of key-value pairs starting with ? and separated via &.
Numbers
A decimal number consists of one or more decimal digits. The first digit is not permitted to be zero unless the number consists of a single digit.
number = "0" / non-zero-digit *decimal-digit non-zero-digit = %x31-39 ; "1" to "9" decimal-digit = %x30-39 ; "0" to "9"
Identifiers
An identifier conforms to the definition of an identifier in ASN.1 notation (Clause 12.3 of [[X.680]]). It begins with a lowercase letter and is followed by zero or more letters, digits, and hyphens. A hyphen is not permitted to be the last character, nor is it to be followed by another hyphen. The case of letters in an identifier is significant.
identifier = lowercase *alphanumeric *( "-" 1*alphanumeric ) alphanumeric = uppercase / lowercase / decimal-digit uppercase = %x41-5A ; "A" to "Z" lowercase = %x61-7A ; "a" to "z"
BACnet Vocabulary
This section describes the vocabulary used in BACnet. A protocol binding implementation should use the vocabulary defined in this section to describe the different configurations that can be used to exchange data between Web of Things clients, devices, and services.
This vocabulary is fully defined in the BACnet ontology published together with this document.
Until the JSON-LD context is published, this document will use the example URI
"https://example.org/bacnet"
for the context with the prefix "bacv"
.
URI Variables
URI variable | Description | Assignment | Range |
---|---|---|---|
commandPriority |
Sets priority for WriteProperty | optional |
integer
|
relinquish |
Indicates that the value at this priority should be deleted/relinquished | optional |
boolean
|
covIncrement |
Sets COV increment change for reporting via SubscribeCOVProperty (SubscribeCOV with a property ID) | optional |
number
|
URI Variables are included in the 'bacnet' URI Scheme and are included in the Protocol Binding to communicate PDU option settings for the BACnet driver to use when sending requests.
The following JSON example shows the usage of the BACnet vocabulary for URI Variables, specifically commandPriority, relinquish, and covIncrement. The semantic annotation is optional.
This template could be used to construct the uriVariables element for TD interaction affordances, to enable the application to communicate URI variables to the BACnet driver, using a consistent format according to the schema constraints below.
These variables may be also fixed in the href and not exposed via URI variables, causing the BACnet driver to use a constant value for this parameter.
Some aspects were identified as improvement candidates in TD.next, which may result in breaking changes regarding URI variables:
- Showing the link of URI variables to exactly which operations they apply to.
- Exposing protocol-related parameters from Form-level instead of InteractionAffordance-level.
- Providing a standardized set of parameters.
- Having the ability to use parameters also in other places than the href
{ "@context": ["https://www.w3.org/2022/wot/td/v1.1", { "bacv": "https://example.org/bacnet" }], "uriVariables": { "commandPriority": { "@type": "bacv:CommandPriority", "type": "integer", "enum": [1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], "default": 16 }, "covIncrement": { "@type": "bacv:CovIncrement", "type": "number", "minimum": 0 }, "relinquish": { "@type": "bacv:Relinquish", "type": "boolean", "default": false } } }
Form terms
Vocabulary term | Description | Range | Domain |
---|---|---|---|
bacv:usesService |
Indicates the BACnet service to use |
string
(one of |
hctl:Form |
bacv:isISO8601 |
This data uses ISO8601 format |
boolean
|
bacv:Sequence |
bacv:hasBinaryRepresentation |
Points to the binary representation of the resource |
string
(one of |
bacv:OctetString |
bacv:hasMember |
Member of a Sequence or List | bacv:DataType | bacv:Sequence, bacv:List |
bacv:hasNamedMember |
Named Member of a Sequence or Choice | bacv:NamedMember | bacv:Sequence, bacv:Choice |
bacv:hasFieldName |
Name of a Named Member of a Sequence or Choice |
string
|
bacv:NamedMember |
bacv:hasContextTag |
Context Tag for a Named Member of a Sequence or Choice |
boolean
|
bacv:NamedMember |
bacv:hasMapEntry |
An value map entry mapping an Enumerated value to text | bacv:ValueMapEntry | bacv:ValueMap |
bacv:hasLogicalVal |
Logical Value for a ValueMap |
integer or
string or
boolean
|
bacv:ValueMapEntry |
bacv:hasProtocolVal |
Protocol Value for a ValueMap |
integer
|
bacv:ValueMapEntry |
bacv:hasValueMap |
Value map for an Enumeration | bacv:ValueMap | bacv:Boolean, bacv:Enumerated, bacv:Unsigned, bacv:BitString |
Mappings
This section describes the strategies and default values to use protocol-specific concepts within the WoT Interaction model.
Default Operation Mappings
Operation | Default Binding | Variables and parameters |
---|---|---|
readproperty |
"bacv:usesService": "ReadProperty" |
|
writeproperty |
"bacv:usesService": "WriteProperty" |
commandPriority, relinquish |
observeproperty |
"bacv:usesService": "SubscribeCOV" |
covIncrement |
unobserveproperty |
"bacv:usesService": "SubscribeCOV" |
|
observeproperty |
"bacv:usesService": "SubscribeCOV" |
Note: BACnet driver must handle Status Flags |
unobserveproperty |
"bacv:usesService": "SubscribeCOV" |
WoT Data Schema and BACnet Types
BACnet has its own data model, which cannot always be derived from the data schema of the interaction affordances. Hence the type information is amended in the Form (column 3) of the protocol binding while staying conceptually conformant to the WoT Data Schema (column 2). The goal here is to abstract BACnet's data model into a Web-like JSON-based data model, by still keeping the wire compatibility on the protocol. The table below shows how the mappings are done, comprehensively for all BACnet types.
BACnet Type | WoT Data Schema | BACnet Type Description under the Form |
---|---|---|
bacv:SequenceOf |
{ "type": "array" } |
{ "bacv:hasDataType": { "@type": "bacv:SequenceOf", "bacv:hasMember": { "$comment": "Data type of the
element", "@type": "bacv:..." } } }
|
bacv:Sequence |
{ "type": "object" }
For the special case where a DateTime object is modeled as a Sequence "bacv:isISO8601": true :
{ "type": "string", "pattern":
"^((([1-9][0-9]*)?[0-9]{4})|\\*)-((1[0-2]|0[1-9])|O|E|\\*)-((3[01]|0[1-9]|[12][0-9])|O|E|\\*)(\\s*\\([1-7\\*]\\))?T((2[0-3]|[01][0-9])|\\*):([0-5][0-9]|\\*):([0-5][0-9]|\\*)(\\.[0-9]+)?(Z|[+-]\\d\\d:\\d\\d)?$",
"$comment": "A recurrence rule format based on ISO8601, specifies repeating dates and times using
year, month, day, hour, minute, second, and optionally day of week (1: Mon, 7: Sun). Supports
wildcards: 'O' for odd values, 'E' for even values, and '*' for any valid value in each component." }
Please see bacv:Date and bacv:Time about the details of wildcards.
|
{ "bacv:hasDataType": { "@type": "bacv:Sequence", "bacv:isISO8601": true, "bacv:hasNamedMember": [ {
"bacv:hasFieldName": "date", "bacv:hasDataType": { "@type": "bacv:Date" } }, { "bacv:hasFieldName":
"time", "bacv:hasDataType": { "@type": "bacv:Time" } } ] } }
|
bacv:List |
{ "type": "array", "uniqueItems": true } |
{ "bacv:hasDataType": { "@type": "bacv:List", "bacv:hasMember": { "@type": "bacv:..." } } }
|
bacv:Choice |
{ "oneOf": [ { "type": "..." }, { "type": "..." }, { "...": "..." }, ]}
|
{ "bacv:hasDataType": { "@type": "bacv:Choice", "bacv:hasNamedMember": [ { "bacv:hasFieldName": "...",
"bacv:hasContextTag": 1, "bacv:hasDataType": { "@type": "bacv:..." } }, { "bacv:hasFieldName": "...",
"bacv:hasContextTag": 2, "bacv:hasDataType": { "@type": "bacv:..." } } ] } }
|
bacv:Date |
{ "type": "string", "pattern":
"^((([1-9][0-9]*)?[0-9]{4})|\\*)-((1[0-2]|0[1-9])|O|E|\\*)-((3[01]|0[1-9]|[12][0-9])|O|E|\\*)(\\s*\\([1-7\\*]\\))?$",
"$comment": "A recurrence rule format based on ISO8601, specifies repeating dates using year, month,
day, and optionally day of week (1: Mon, 7: Sun). Supports wildcards: 'O' for odd days, 'E' for even
days, and '*' for any valid value in each component." }
|
{ "bacv:hasDataType": { "@type": "bacv:Date" } }
|
bacv:Time |
{ "type": "string", "pattern":
"^((2[0-3]|[01][0-9])|\\*):([0-5][0-9]|\\*):([0-5][0-9]|\\*)(\\.[0-9]+)?(Z|[+-]\\d\\d:\\d\\d)?$",
"$comment": "A recurrence rule format based on ISO8601, specifies repeating times using hour, minute,
and second. Supports the '*' wildcard for any valid value in each component." }
Examples:
|
{ "bacv:hasDataType": { "@type": "bacv:Time" } }
|
bacv:WeekNDay |
{ "type": "string", "pattern":
"^((1[0-2]|0[1-9])|O|E|\\*)-(01|08|15|22|29|L7|B7|B14|B21|\\*)-([1-7]|\\*)$", "$comment": "Custom
WeekNDay string: first part month 01-12, O for odd, E for even, * for any; second part week of month:
beginning on 01st, 08th, the last 7 days, they days before last 7 days, last 14 days.., or any; third
part day of week: 1-7 (Mon-Sun) or any" }
|
{ "bacv:hasDataType": { "@type": "bacv:WeekNDay" } }
|
bacv:Unsigned |
{ "type": "integer", "minimum": 0 } |
{ "bacv:hasDataType": { "@type": "bacv:Unsigned" } }
|
bacv:Signed |
{ "type": "integer" } |
{ "bacv:hasDataType": { "@type": "bacv:Signed" } }
|
bacv:Real |
{ "type": "number" } |
{ "bacv:hasDataType": { "@type": "bacv:Real" } }
|
bacv:Double |
{ "type": "number" } |
{ "bacv:hasDataType": { "@type": "bacv:Double" } }
|
bacv:Boolean |
{ "type": "boolean" } |
{ "bacv:hasDataType": { "@type": "bacv:Boolean" } }
|
bacv:Enumerated |
{ "$comment": "Contains the possible enum members, as strings or integers", "enum": [] }
|
{ "bacv:hasDataType": { "@type": "bacv:Enumerated" } }
|
bacv:String |
{ "type": "string" } |
{ "bacv:hasDataType": { "@type": "bacv:String" } }
|
bacv:OctetString |
{ "type": "string", "pattern":
"^(([0-9A-F]{2}-?)+|(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{4}|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{2}={2}))$",
"$comment": "Binary data encoded in hex, dotted decimal (e.g., IPv4 address) or base64" }
|
{ "bacv:hasDataType": { "@type": "bacv:OctetString", "bacv:hasBinaryRepresentation": "..." } }
|
bacv:BitString |
{ "type": "array", "items": { "$comment": "Contains the possible bit numbers", "enum": [] }
"uniqueItems": true }
|
{ "bacv:hasDataType": { "@type": "bacv:BitString" } }
|
bacv:Any |
The type field should be left out completely. This is allowed according to
WoT Data Schema Vocabulary Definitions.
|
{ "bacv:hasDataType": { "@type": "bacv:Any" } }
|
bacv:Null |
{ "type": "null" } |
"bacv:hasDataType": { "@type": "bacv:Null" }
|
bacv:ObjectIdentifier |
{ "type": "string" "format": "iri-reference", "$comment": "BACnet Object Identifier is to be converted
to an IRI, using the href schema of this protocol binding" }
|
{ "bacv:hasDataType": { "@type": "bacv:ObjectIdentifier" } }
|
BACnet contentType
BACnet has only one encoding format based on ASN.1
for transporting data. Since there is no other option and to avoid falling back to the
default
of application/json
, contentType
shall always be set to
application/octet-stream
.
Mappings related to events
BACnet has an extensive alarm model; see [[BACnet]] Chapter 13. Alarm and Event Services for a complete description. The fundamental aspects are as follows:
- There are three event states: Normal (no event); OffNormal (e.g., operating temperature outside of the normal range); and Fault (e.g., defective sensor). Events are sent for transitions, including self-transitions and toNormal transitions.
- Objects can monitor themselves for event states (intrinsic reporting), or dedicated other objects can monitor other objects (algorithmic reporting)
- Alarms are always sent via Notification Class objects, which keep lists of alarm recipients, i.e., alarm subscriptions are always of the Notification Classes; it is not possible to receive alarms only from some of the objects linked to a given Notification Class. In the typical case, a client will register to all Notification Classes of a BACnet Device.
- Some alarms can be configured as "acknowledgement required". Acknowledgement is defined by BACnet as a manual action performed by a human operator, and is separate from the confirmation of the successful reception of the event notification.
The picture below demonstrates these key aspects and how they are mapped to the WoT concepts.
The key aspects of the mapping to WoT are:
- Each Notification Class is mapped to an EventAffordance. This maps directly to the subscription granularity.
- Event Enrollment objects used for algorithmic reporting are not explicitly mapped; they are not relevant to the WoT consumer, per se.
-
Special event-related actions (getting current events and acknowledging events) are mapped to
ActionAffordances.
The WoT consumer does not need to know the details of BACnet alarming; it can simply interact with the InteractionAffordances of the Thing, according to their data schemata. However, the consumer does need to have an understanding of the relations between the InteractionAffordances:
-
GetCurrentEvents
action can be used to fetch all current events of the thing, which would not be received just by subscribing to events. This action delivers the same payload as the EventAffordance's data. -
AcknowledgeEvent
action can be used for all events (including the ones received viaGetCurrentEvents
), where theackReq
field is true. Here thesource
field of the event should match theeventSource
field ofAcknowledgeEvent.input
.
Future work on Manageable Affordances will improve this by modelling the relations between InteractionAffordances.
-
- Thing is assumed to be mapped to the BACnet device. While this is simple, it is not a mandatory decision. In specific cases, one BACnet device can correspond to multiple things, and even vice-versa.
In contrast to PropertyAffordances, which are generically mapped to BACnet, interactions related to events come with a specific payload specified by the BACnet standard. The flexibility that would be possible, for example, for an HTTP longpoll-based event notification with a custom HTTP body, is not possible in BACnet context. Thus, it is necessary for the WoT consumer to work with a given logical data schema that is influenced by the BACnet standard. Still, this binding takes an approach to define the data schemata of the InteractionAffordances as independently as possible from BACnet, in order to avoid a direct dependency of WoT consumers on a specific protocol.
With this background, this binding takes some simplifying design decisions:
-
When the BACnet payload is fixed by the BACnet standard for a given service — e.g., event subscription, or
data or alarm acknowledgement — the binding doesn't model the BACnet data with
bacv:hasDataType
. - Some features of the BACnet alarm model are abstracted away: filtering alarms based on day of week, time of day, and event state; and using unconfirmed event notifications. If these become necessary, they can be added in a future version.
- Payloads containing three event states (Normal, OffNormal, Fault) are split into simpler payloads for each event state. For instance, GetEventInformation sends one array item for each BACnet object containing three flags, three timestamps, three acknowledge flags, etc., for each possible event state. This binding splits that payload in the ActionAffordance's output into multiple payloads for each active event, with one timestamp, acknowledge flag, etc. This approach makes the output similar to the EventAffordance data payload and removes the three-event state-based BACnet design from the WoT Consumer's view.
The resulting data payloads look like this:
Data | WoT Model | BACnet Model | Remarks |
---|---|---|---|
EventAffordance.subscription | Not modeled; subscription is implicit |
|
The simplifications below allow to omit the WoT Data Model.
|
EventAffordance.cancellation | Not modeled; cancellation is implicit | Same as EventAffordance.subscription | Same as EventAffordance.subscription |
EventAffordance.data |
|
|
We focus only on ConfirmedEventNotification; unconfirmed event notifications are currently not supported. |
GetCurrentEvents: ActionAffordance.input |
|
|
|
GetCurrentEvents: ActionAffordance.output |
|
|
As indicated above, the BACnet payload and the ActionAffordance output differ, especially around how events are grouped. In the BACnet payload, one list item represents one object with its three possible events, which is reflected in Event State, Acknowledged Transitions, Event Timestamps, and Priorities. This is to be processed and split into one standalone event for every current event, or past unacknowledged event, of this object, with corresponding timestamp, priority, etc. |
AcknowledgeEvent: ActionAffordance.input |
|
|
Process Identifier is to be set by WoT Consumer corresponding to what it has entered at subscription time. |
Detailed schemata of these payloads can be found in the JSON Schema linked in this file's header. Examples below will be helpful in understanding the details.
Examples
This section will present a set of examples of how the terms defined in this document can be used to describe and configure a Form.
[[[#example-readproperty]]] shows a property with the object type
analog-input
, object instance 1
, and property identifier present-value
in
the scope of a BACnet device with device instance number 5
.
{ "@context": ["https://www.w3.org/2022/wot/td/v1.1", { "bacv": "https://example.org/bacnet" }], ... "properties": { "analog1": { "type": "number", "readOnly": true, "forms": [{ "op": [ "readproperty" ], "href": "bacnet://5/0,1/85", "contentType": "application/octet-stream", "bacv:usesService": "ReadProperty", "bacv:hasDataType": { "@type": "bacv:Real" } }] } } }
[[[#example-uri-variables-with-forms]]] demonstrates how the uriVariables
in the affordance-level
can be used and how they need to be represented in a forms
element. The
"bacv:covIncrement"
indicates that the URI variable set by the Consumer application will be used to
notify the Consumer only if the property value changes more than the set amount by the URI variable.
{ "@context": ["https://www.w3.org/2022/wot/td/v1.1", { "bacv": "https://example.org/bacnet" }], ... "properties": { "analog1": { "type": "number", "readOnly": true, "uriVariables": { "observeIncrement": { "@type": "bacv:covIncrement", "type": "number", "minimum": 0 } }, "forms": [{ "op": [ "observeproperty" ], "href": "bacnet://5/0,1/85?covIncrement={observeIncrement}", "contentType": "application/octet-stream", "bacv:usesService": "SubscribeCOV", "bacv:hasDataType": { "@type": "bacv:Real" } }] } } }
[[[#example-uri-variable-for-writing]]] demonstrates how the uriVariable
in the affordance-level
can be used to model a writing priority in a forms
element. The
"bacv:CommandPriority"
indicates that the URI variable set by the Consumer application will be
relayed as the priority argument in the WriteProperty request. "bacv:Relinquish"
indicates whether
the user wants to relinquish the property value at the given command priority. For other operations (e.g.,
readproperty
, observeproperty
, unobserveproperty
) these URI variables are
irrelevant, so the corresponding forms do not use them. If the URI variables are also irrelevant for the
writeproperty
operation (e.g., when the BACnet object doesn't have a priority array), they can also
be left out of the writeproperty
form, and the URI variables can be omitted completely. The URI
variables come with defaults, so if the Consumer doesn't provide them, the defaults of
writePriority=16
and relinquish=false
will be used. Another possibility is to fix the
writing priority in the TD by giving a fixed commandPriority
in the href
of the form,
and not exposing its value via a URI variable. These decisions belong to the TD author.
When relinquish is set to true
, WoT consumer will ignore the value given to the
writeproperty
operation and write NULL
instead. This clears the value at the
specified priority, and is the only way of allowing a lower priority to take effect.
{ "@context": ["https://www.w3.org/2022/wot/td/v1.1", { "bacv": "https://example.org/bacnet" }], ... "properties": { "analog1": { "type": "number", "readOnly": false, "writeOnly": false, "observable": true, "uriVariables": { "writePriority": { "@type": "bacv:CommandPriority", "type": "integer", "enum": [1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], "default": 16 }, "relinquish": { "@type": "bacv:Relinquish", "type": "boolean", "default": false } }, "forms": [ { "op": [ "readproperty", "observeproperty", "unobserveproperty" ], "href": "bacnet://5/2,1", "contentType": "application/octet-stream", "bacv:hasDataType": { "@type": "bacv:Real" } }, { "op": [ "writeproperty" ], "href": "bacnet://5/2,1?commandPriority={writePriority}{&relinquish}", "contentType": "application/octet-stream", "bacv:usesService": "WriteProperty", "bacv:hasDataType": { "@type": "bacv:Real" } } ] } } }
[[[#example-enum-mapping]]] shows how an enum
in the Data Schema is extended in the
forms
to make it understandable by BACnet drivers.
{ "@context": ["https://www.w3.org/2022/wot/td/v1.1", { "bacv": "https://example.org/bacnet" }], ... "properties": { "multistate1": { "type": "string", "enum": ["on", "off", "auto", "manual"], "readOnly": true, "forms": [{ "op": [ "readproperty" ], "href": "bacnet://5/14,1/85", "contentType": "application/octet-stream", "bacv:usesService": "ReadProperty", "bacv:hasDataType": { "@type": "bacv:Enumerated", "bacv:hasValueMap": [ { "bacv:hasProtocolVal": 1, "bacv:hasLogicalVal": "on" }, { "bacv:hasProtocolVal": 2, "bacv:hasLogicalVal": "off" }, { "bacv:hasProtocolVal": 3, "bacv:hasLogicalVal": "auto" }, { "bacv:hasProtocolVal": 4, "bacv:hasLogicalVal": "normal" } ] } }] } } }
[[[#example-event]]] shows the InteractionAffordances for using BACnet events:
- One ActionAffordance for the Device Object for GetEventInfo, named as GetCurrentEvents
- One ActionAffordance for the Device Object for AcknowledgeAlarm, named as AcknowledgeEvent
- One EventAffordance for every Notification Class object
{ "@context": ["https://www.w3.org/2022/wot/td/v1.1", { "bacv": "https://example.org/bacnet" }], ... "actions": { "GetCurrentEvents": { "input": { "type": "object", "properties": { "lastReceivedSource": { "type": "string", "format": "iri-reference" } } }, "output": { "type": "object", "properties": { "currentEvents": { "type": "array", "items": { "type": "object", "properties": { "source": { "type": "string", "format": "iri-reference" }, "time": { "type": "string", "format": "date-time" }, "prio": { "type": "integer" }, "ackReq": { "type": "integer", "enum": [ 0, 1 ], "default": 0 }, "toState": { "type": "string", "enum": [ "fault", "offnormal", "normal" ] }, "eventType": { "type": "string", "enum": [ "regular", "ack", "syncrequired" ], "default": "regular" }, "origSource": { "type": "string", "format": "iri-reference" }, "message": { "type": "string" }, "values": { "type": "object" }, "inactive": { "type": "integer", "enum": [ 0, 1 ], "default": 0 } }, "required": [ "source", "time", "prio", "toState" ] } } }, "moreEvents": { "type": "boolean" }, "required": [ "currentEvents", "moreEvents" ] }, "forms": [ { "href": "bacnet://1/8,1", "contentType": "application/octet-stream", "op": [ "invokeaction" ], "bacv:usesService": "GetEventInfo" ] } ] }, "AcknowledgeEvent": { "input": { "type": "object", "properties": { "source": { "type": "string", "format": "iri-reference" }, "toState": { "type": "string", "enum": [ "fault", "offnormal", "normal" ] }, "eventTime": { "type": "string", "format": "date-time" }, "ackSource": { "type": "string" }, "ackTime": { "type": "string", "format": "date-time" }, "required": ["source", "ackState", "eventTime", "ackSource", "ackTime"] } }, "forms": [ { "href": "bacnet://1/8,1", "contentType": "application/octet-stream", "op": [ "invokeaction" ], "bacv:usesService": "AcknowledgeAlarm" } ] } }, "events": { "NotifCl2": { "title": "Highest priority notification with acknowledge", "data": { "type": "object", "properties": { "source": { "type": "string", "format": "iri-reference" }, "time": { "type": "string", "format": "date-time" }, "prio": { "type": "integer" }, "message": { "type": "string" }, "ackReq": { "type": "integer", "enum": [0, 1], "default": 0 }, "toState": { "type": "string", "enum": [ "fault", "offnormal", "normal" ] }, "eventType": { "type": "string", "enum": ["regular", "ack", "syncrequired"], "default": "regular" }, "origSource": { "type": "string", "format": "iri-reference" }, "values": { "type": "object" }, "inactive": { "type": "integer", "enum": [0, 1], "default": 0 } }, "required": [ "source", "time", "prio", "toState" ] }, "forms": [ { "href": "bacnet://2/15,2/102", "contentType": "application/octet-stream", "op": [ "subscribeevent", "unsubscribeevent" ] } ], }, } }
The resulting example payloads are available below.
{ "source": "bacnet://5/0,20", "time": "2024-07-30T12:33:00+00:00", "prio": 150, "message": "Equipment overheat", "ackReq": 1, "toState": "offnormal", "values": { "deadband": 0.5, "exceedLim": 50, "exceedValue": 55, "statusFlags": [0, 0, 0, 0] } }
[[[#example-event-data]]] shows an event notification because of an overheating piece of equipment. Event
message
gives this as a useful hint; prio
is set accordingly.
values
object gives additional details about the event — that the current value is 55, and the
exceeded limit was 50. The unit of these values can be found at the source
object that triggered
this event. Because overheating events are important for the operator to notice, ackReq
is set, so
the operator definitely will see the event, even after the overheating condition is over.
{ "source": "bacnet://5/0,20", "toState": "offnormal", "eventTime": "2024-07-30T12:33:00+00:00", "ackSource": "Management Station Operator Acknowledge", "ackTime": "2024-07-30T13:15:00+00:00" }
When the operator sees this event and decides to acnowledge it, management station will send the payload shown in [[[#example-acknowledgeevent-input]]]. This can happen before or after the event is over.
{ "currentEvents": [ { "source": "bacnet://5/0,12", "time": "2024-07-21T20:14:00+00:00", "prio": 200, "ackReq": 0, "toState": "fault" }, { "source": "bacnet://5/0,20", "time": "2024-07-30T12:33:00+00:00", "prio": 150, "message": "Equipment overheat", "ackReq": 0, "toState": "offnormal", "values": { "deadband": 0.5, "exceedLim": 50, "exceedValue": 55, "statusFlags": [0, 0, 0, 0] } } ], "moreEvents": false }
If the event client wants to get a list of all events of the device, it will invoke the GetCurrentEvents action,
and will receive te payload shown in [[[#example-getcurrentevents-output]]]. Here the event notification about
the overheating and a past fault event can be seen. This is useful for synchronizing the events known on the
client to the events that are on the device, in case of temporary disconnection, reboot of client or device,
etc. Please note that in the second alarm, the ackReq
value turned to 0, because this event was
acknowledged by the operator in the meantime.