BACnet
Synopsis
Director reads one BACnet device over BACnet/IP (ASHRAE 135 Annex J, UDP port 47808): a controller of a building management system, an air handling unit, a chiller, a boiler, a VAV box, an energy meter, a fire or an access panel, or the front end of a BMS such as Johnson Controls Metasys, Siemens Desigo, Honeywell, Schneider, or Tridium Niagara. Nothing is installed on the device or beside it: Director speaks BACnet as an operator workstation does, and writes what it reads as ECS records.
It only reads. It never sends a WriteProperty or a WritePropertyMultiple, never commands, restarts, time-synchronizes, or silences a device, and creates or deletes no object. What it sends is Who-Is, ReadProperty, ReadPropertyMultiple, SubscribeCOV (and its cancellation), the answers a client owes, and Register-Foreign-Device. What it leaves at the far end is a subscription to an object's changes of value, which it cancels when it stops, and its registration with a BBMD as a foreign device, which it lets run out. See What It Sends.
One device, one BACnet device. Monitoring forty controllers takes forty devices. What a device reads is declared by its datasets: a BACnet device's objects have identifiers, not meanings, so a table of objects is your own, and the inventory dataset finds what there is to declare.
Unlike the other polled devices, it binds a port. BACnet/IP's devices answer to UDP port 47808 on both sides, so the BACnet devices of one Director share one socket, and any number of them can use 47808. A device of any other type on that port is refused, and the other way round. See One Socket per Director.
The device runs on Director only; no agent runs it. One Director owns each device: two would read it twice, and take two of the few subscriptions a controller has room for.
The BACnet device needs Director 2.4.1 or later. With no Director or cluster at that version selected, the wizard shows BACnet devices are unavailable on directors or clusters below version 2.4.1 and the device cannot be created.
Schema
- id: <numeric>
name: <string>
description: <string>
type: bacnet
tags: <string[]>
pipelines: <pipeline[]>
status: <boolean>
properties:
address: <string>
port: <numeric>
device_port: <numeric>
device_instance: <numeric>
network_number: <numeric>
mac_address: <string|numeric>
broadcast_address: <string>
bbmd_address: <string>
bbmd_port: <numeric>
bbmd_ttl: <numeric>
bind_address: <string>
timeout: <numeric>
max_retries: <numeric>
segmentation: <boolean>
max_segments: <numeric>
max_apdu_length: <numeric>
definitions: <definition[]>
Configuration
The following fields are used to define the device:
Device
| Field | Required | Default | Description |
|---|---|---|---|
id | Y | - | Unique numeric identifier |
name | Y | - | Device name |
description | N | - | Optional description |
type | Y | - | Must be bacnet |
tags | N | - | Optional tags |
pipelines | N | - | Optional pre-processor pipelines |
status | N | true | Enable/disable the device |
The Device Read
The BACnet device is named by its address, by its instance, or by both. One of address and device_instance is required.
| Field | Required | Default | Description |
|---|---|---|---|
address | N* | - | Host name or IPv4 address of the BACnet device, or of the BACnet router it sits behind. A wildcard, a broadcast or a multicast address, an IPv6 address, and a port written into the address are refused: BACnet/IP is IPv4's, and the port is its own field. |
device_port | N | port | The UDP port the device at address is reached on, when it is not the one Director binds, from 1 to 65535. Not used, and the device's log says so, for a device found by its instance. |
device_instance | N* | - | The device's instance (its Device object's), from 0 to 4194302. Without an address, a Who-Is finds the device by it. With one, the device at the address must be this instance, and is refused when it has another. See Finding the Device. |
network_number | N | - | From 1 to 65534. The number of the device's network behind the BACnet router at address, with mac_address. With a device_instance and no address, it limits the Who-Is to that network. |
mac_address | N | - | The device's address on network_number, behind the router at address; both are required with it. An MS/TP station's number, 0 to 254, as its tools show it (12); the octets of any address in hexadecimal, after 0x or with a colon or a hyphen between them (0x0C, 00:1A:2B:3C:4D:5E), seven at most; or the address and port of a BACnet/IP device (192.0.2.20:47808). Digits alone are a station's number, never hexadecimal. |
* One of address and device_instance is required.
The Director's Socket
| Field | Required | Default | Description |
|---|---|---|---|
bind_address | N | 0.0.0.0 | The IPv4 address of Director the socket is bound to; every address by default, which is what to leave it at. See A Bind Address on Linux. |
port | N | 47808 | The UDP port the socket is bound to, BACnet/IP's, and the one the device is reached on unless device_port names another. From 1 to 65535. Every BACnet device of a Director that names the same bind_address and port shares the socket. |
broadcast_address | N | 255.255.255.255 | Where a Who-Is is broadcast: the IPv4 broadcast address of the subnet (192.0.2.255), with a port after a colon when the network's is not port. The default goes out of the interface the system chooses: on a Director with more than one, name the subnet's own. Not used, and the device's log says so, with a BBMD. |
bbmd_address | N | - | Host name or IPv4 address of the BBMD Director registers with as a foreign device, to reach the devices of another subnet. See Through a BBMD. |
bbmd_port | N | 47808 | The BBMD's UDP port, from 1 to 65535 |
bbmd_ttl | N | 300 | Seconds a registration lasts, from 30 to 65535. It is made again when half of it has passed. |
Requests
| Field | Required | Default | Description |
|---|---|---|---|
timeout | N | 3 | Seconds an answer is waited for, from 1 to 60: the APDU timeout. A device of a slow MS/TP line behind a router takes several. |
max_retries | N | 3 | How often a request that is not answered is sent again, from 0 to 10: the number of APDU retries. A device that does not answer is given up after timeout × (max_retries + 1) seconds. |
segmentation | N | true | Whether Director takes an answer in segments. Off, it asks for answers that fit one message, and reads an answer that does not in smaller requests. |
max_segments | N | 64 | How many segments of an answer Director takes: 2, 4, 8, 16, 32, or 64. Not used, and the device's log says so, with segmentation off. |
max_apdu_length | N | 1476 | The longest message Director takes: 50, 128, 206, 480, 1024, or 1476, BACnet/IP's by default. |
A device that fails these checks does not start, and its state is Configuration error; the message names the field. Examples are no device to read, an address or a port that is not one, an instance or a network out of range, a MAC without its network and its router, a network with an address and no MAC, or a value that is not a number or not one of its choices. A number is read whole: NaN, an infinity, or a fraction is refused, never converted.
Connection
One Socket per Director
BACnet/IP has no connection. A request leaves Director's UDP port and the device answers to that port, and what a device sends unasked, its I-Am to a Who-Is and the changes of value it reports, comes to the port too. The standard's port is 47808 on both sides, and a device that answers a broadcast Who-Is answers with a broadcast to it.
So Director binds one socket for each bind_address and port, opened by the first BACnet device that asks for it and closed when the last lets go of it, and hands each answer, I-Am, and change of value to the device it is for. An answer is taken only from the address its request went to, and a change of value only from the address its subscription was made at.
One request at a time goes to a BACnet device, whichever of the Director's devices asks: a controller answers a few at once at most, and one on an MS/TP line one.
Ports. The BACnet devices of a Director that bind one port share it, any number of them on 47808. The console's port check lets BACnet devices share a port, and refuses a device of another type that binds the port beside them, such as a UDP listener on 47808, and a BACnet device on a port a listener holds. Two BACnet devices that bind one port, one on every address (0.0.0.0) and one on a single address, are two sockets the system does not keep apart: the console refuses the second, and a Director given both from a configuration file starts the first and puts the second in Configuration error, naming both addresses. Two different addresses of the Director, one each, are two sockets.
A port another program on the Director's host holds, such as a BACnet workstation or a second BMS service, cannot be bound: the device is in Initialization error until it can. A Director that must run beside one binds another port, and reaches its devices by their addresses or through a BBMD.
A Bind Address on Linux
A Director on two BACnet networks keeps bind_address at 0.0.0.0, one socket for both, and names each network's own broadcast_address on the devices of that network: where a broadcast goes is the device's setting, not the socket's.
Binding one address each is not the way on Linux, where a UDP socket bound to one address receives only the datagrams sent to that address. A broadcast, which is what most devices answer a Who-Is with, does not reach it, so a device found by its instance is not found, and a network's inventory finds only the device read. Finding devices by instance and a network inventory then need a BBMD, or the default 0.0.0.0. Devices read by their address, and those reached through a BBMD, which sends Director their I-Ams itself, are read either way. Windows hands a socket bound to one address the broadcasts of that address's subnet.
Container Directors
On a container Director whose platform publishes no UDP port, which is the case on Azure Container Apps and Azure Container Instances, a device read by its address works: its answers come back the way its requests went. Finding a device by its instance and receiving changes of value need inbound UDP, which those platforms do not publish. See Device Listener Ports.
Finding the Device
- By its address. The device at
addressanddevice_portis asked which device it is (its Device object under the wildcard instance4194303), then for the longest message it takes, whether it sends answers in segments, and its vendor. An older device that does not answer for that instance is asked with a Who-Is sent to it alone; one that answers neither cannot be read until itsdevice_instanceis named. With adevice_instancenamed, a device that has another is in Configuration error, which says that the device at the address is not the one named. A host name is looked up every time the device is looked for. - By its instance. A Who-Is for that instance alone is broadcast to
broadcast_address, for every network (routers pass it on), or fornetwork_numberalone when it is named. The device's I-Am says where it is, behind a router with its network and its MAC. The Who-Is is sent again everytimeout,max_retriestimes, and then the device is not found: Connection error, with what to check. - Behind a BACnet router. A device of an MS/TP line, or of any network behind a BACnet router, is read through the router: every request names the device's network and its MAC. Named by
address,network_number, andmac_address, it is asked at once; found by its instance, its I-Am names all three. A router that cannot pass a request on says so, and the device's log says that it has no route to that network, with the advice to checknetwork_number.
A device that stopped answering is looked for again at the next reading, so a device found by its instance follows a change of address: it is read at its new address, and subscribed to anew.
Through a BBMD
A broadcast does not leave Director's subnet. To find and hear the devices of another subnet, Director registers with that network's BBMD as a foreign device (Register-Foreign-Device, Annex J.5). Its Who-Is then goes to the BBMD, which sends it on, and the BBMD passes the I-Ams back; Director takes them from that BBMD alone. Requests and answers go straight between Director and the device, as IP routes them.
The registration lasts bbmd_ttl seconds and is made again when half of it has passed. One the BBMD refuses or does not answer is tried again after a second, then after twice the last wait, up to a minute; a BBMD that has forgotten Director is registered with again at once. The devices of a Director that name one BBMD share one registration, made for the shortest of their times to live. A device whose BBMD does not register Director is in Connection error, with the BBMD's answer. The BBMD's entry is not deleted when the device stops: it runs out.
Datasets
The device's definitions are its datasets. In the console they are the datasets and profiles assigned to the device; see Datasets and Profiles. In a configuration file each dataset is a definition named by its type:
| Dataset | Definition | Definition ID | Kind | One record per |
|---|---|---|---|---|
object | bacnet_object_collector | 33150 | metric | reading of the dataset's table |
cov | bacnet_cov_collector | 33151 | metric | change of value the device reports, or reading of an object it refuses to report |
inventory | bacnet_inventory_collector | 33100 | inventory | object of every device a sweep reads |
- Any number of object and change of value datasets. Each is a table of its own, so a device reads as many as are assigned.
- One inventory per device. A second inventory assigned to the same device is left out, and the device's log says so.
- Pipelines. A dataset's records enter the pre-processing pipeline the dataset selects, or the device's when it selects none.
- Nothing assigned is allowed. The device then only checks, every minute, that its BACnet device answers, so its connection state stays current, and writes nothing.
- A dataset that cannot be read is left out, with a warning in the device's log that names it and says why: a table that is not a name, an object or a property that is not one, or an object or a name declared twice. The rest of the device runs. With every dataset assigned to it left out, the device stops with Configuration error.
Objects
A bacnet_object_collector dataset is a table of the device's objects, whose properties are read every interval seconds, all in one reading, which is one record.
| Field | Required | Default | Description |
|---|---|---|---|
table | Y | - | The name the dataset's objects sit under in a record (see Records): letters, digits, and underscores, at most 64, not starting with a digit. confirmed, name, object, source, table, and time_remaining are reserved. |
interval | N | 60 | Seconds between readings, 1 or more |
labels | N | - | An object of names and values written on every record as ECS labels. At most 32, a value at most 256 characters. |
pipeline_name | N | the device's | The pre-processing pipeline the dataset's records enter |
A dataset's inputs are its objects, at most 1024:
| Field | Required | Description |
|---|---|---|
name | N | The name the object's values are written under: letters, digits, and underscores, at most 64, not starting with a digit, once in the table. Left out, it is the object's type and instance (analog_input_3; type_300_3 for a vendor's own type). |
object | Y | The object's type and instance, with a comma, a colon, or a space between them. The type by its name in any way it is written (analog-input,3, Analog_Input:3, analogInput 3), by a short name (AI, AO, AV, BI, BO, BV, MSI, MSO, MSV, also MI, MO, MV, and DEV; AI3 too), or by its number, which names a vendor's own (300,3). The instance is 0 to 4194302. An object is declared once in a table. |
properties | N | The properties read of it: a list of their names in any way they are written (present-value, Present_Value, presentValue) or of their numbers, or text with a comma between them. present-value when left out or empty; at most 16, each once, never all. |
Changes of Value
A bacnet_cov_collector dataset is the same table, subscribed to with SubscribeCOV: the device sends an object's value when the subscription is made and whenever it changes, by its own rule (a binary or a multi-state object at any change, an analog one when it moves by its COV increment). Each notification is a record. Nothing is asked for between two changes.
| Field | Required | Default | Description |
|---|---|---|---|
table, labels, pipeline_name | - | - | As an object dataset's |
confirmed | N | false | Whether the device waits for the acknowledgement of every notification, and sends it again when none comes. Unconfirmed notifications are not sent again when one is lost. |
lifetime | N | 300 | Seconds a subscription lasts, from 60 to 86400. It is renewed when half of it has passed; a device that renews a subscription sends the object's value again, so a quiet object is written once every half lifetime. |
interval | N | 60 | Seconds between the readings of the objects the device refuses to report the changes of |
The inputs are an object dataset's. A notification carries the properties the standard has an object report, its present value and its status flags for most. Of those, the ones properties names are written, so an object that also names status-flags writes both, and one that names units alone writes nothing when notified.
An object whose device refuses a subscription is read on interval instead, as an object dataset's are, and written as a notification would have been, its record saying poll:
- A device that has no SubscribeCOV rejects the first subscription: every object of it is read from then on, and it is not asked again while the device runs.
- An object that reports no changes, or a device with no room for another subscription (a controller has room for a few dozen), answers with an error: that object is read, and asked for a subscription again once a
lifetimehas passed. - A subscription the device does not answer is asked for again 30 seconds later, and the device is in Connection error meanwhile.
Each is said once in the device's log, with the device's answer. A device that waits for changes or for its inventory's next sweep, and has heard nothing for a minute, asks its BACnet device for its system status, and is in Connection error when that is not answered.
Inventory
A bacnet_inventory_collector dataset is what the network has, so that you can see what there is to read: a Who-Is sweep, and for each device that answers, its object list with every object's name, description, and units. One record per object.
| Field | Required | Default | Description |
|---|---|---|---|
interval | N | 3600 | Seconds between sweeps, 60 or more, from the start of one to the start of the next |
scope | N | network | network sweeps every device that answers the Who-Is; device reads only the device this BACnet device reads, and asks the network nothing. |
discovery_timeout | N | 5 | Seconds a sweep listens for I-Ams, from 1 to 120. The Who-Is is sent at its start and at its half. |
device_instance_min, device_instance_max | N | - | The instances a sweep asks for, 0 to 4194302, the first no more than the second. Every device when both are left out. |
max_devices | N | 256 | How many devices a sweep reads, from 1 to 4096: those of the lowest instances, and the device read itself |
max_objects | N | 2000 | How many objects of one device a sweep reads, from 1 to 20000. A sweep reads 100000 objects in all at most. |
labels, pipeline_name | N | - | As an object dataset's |
A network sweep reads every device's object list. A network inventory assigned to several devices runs once for each of them, so every one of them sweeps the network. Assign a scope: network inventory to one device of a network, and use scope: device for an inventory assigned to many.
The device read is in the sweep whether or not its I-Am came, and its objects are marked as the ones this device reads (bacnet.inventory.target). A sweep reads one device after another, one request at a time, in a routine of its own, so the device's values are still read, and its changes written, while the network is swept. Of each device it reads what its Device object says of it (its name, vendor, model, firmware and application software versions, location, and description), its object list, and every object's name, description, and units where the object has them.
From its second sweep on, an inventory marks every record with how the object compares with the last complete sweep (event.action): added, changed (its name, its description, or its units; for a Device object also what the device says of itself), unchanged, or removed, an object that is gone being written once more as the last sweep held it. A device that answers the Who-Is and then no more, or says it is busy, is written as one record with error.type incomplete, and its objects are kept, not removed. The comparison is kept in the Director's memory: a restart, a change to the device or its datasets, or a move to another Director marks nothing at its first sweep.
Requests
- Several properties a request. The properties of a reading are asked for with ReadPropertyMultiple, as many in a request as fit the longest message the device takes, 64 at most. An answer that does not fit is asked for again in halves, and the device is asked for no more than that from then on.
- A property at a time. A device that has no ReadPropertyMultiple rejects it, and is read with ReadProperty, one property a request, from then on. A device that refuses a whole request for one property it does not have has the request split until the property is found, and that property is asked for alone for ten minutes.
- A property the device refuses (an unknown object or property, or denied read access) is left out of the record, and said once in the device's log.
- A device that cannot answer now (busy, being configured, out of resources) ends the reading: the device is in Connection error, is looked for again at the next reading, and is asked then for the same.
- Segments. An answer longer than a message comes in segments, when the device sends them and
segmentationis on. An answer of more segments thanmax_segments, or one whose segments stop coming for atimeout, is given up. Director never sends a request in segments: its requests are cut to the device's longest message. - An object list is asked for whole. When it does not fit and cannot come in segments, its length is read, and then its elements.
Readings
- At start the device reads every object dataset once, makes its subscriptions one object after another, and starts its inventory's first sweep.
- Each object dataset on its own interval after that, from the start of one reading to the start of the next.
- Datasets due at the same time are read one after the other. A reading that takes longer than its interval delays the next, which starts a moment after it ends.
- Subscriptions are renewed when half their lifetime has passed.
Values
| What the device answers | Written as |
|---|---|
| A Real, a Double | A number; a Real as the shortest decimal that reads back as it (72.3). One that is not a number, or is an infinity, has no value. |
| An Unsigned, a Signed | A number |
| A Boolean | true or false |
The present value of a binary input, output, or value (and its alarm-value, feedback-value, relinquish-default) | true for active, false for inactive |
| Status flags | in_alarm, fault, overridden, and out_of_service, a switch each |
| Another bit string | Its bits as text (1010) |
units (and a loop's output-units and the like) | The unit's name as the standard writes it (degrees-celsius, kilowatt-hours, no-units); its number as text for a vendor's own unit, or one newer than the table's 253 |
event-state, reliability, a device's system-status and segmentation-supported, a polarity, an object-type | The standard's name (normal, no-fault-detected, operational); the number as text for a vendor's own |
| Any other enumeration | Its number |
| A character string | Text, read as UTF-8, UCS-2, UCS-4, or ISO 8859-1 as its character set says. The double-byte sets (IBM's and Microsoft's DBCS, JIS X 0208) are not read. |
| An octet string | Its octets in lowercase hexadecimal |
| A date, a time | Text: 2026-10-14, 13:05:09.25; a part that is not specified is * |
| An object identifier | analog-input,3 |
Several values (state-text, priority-array, object-list) | A list of them, a Null among them null |
| A Null alone | No value |
| A structure (a reference, a schedule, a date and time, a list of them) | Not read |
A property with no value, or whose value is not read, is left out of the record, and said once in the device's log for as long as it lasts.
Records
Every record is one ECS 8.11 document with the following fields:
| Field | Value |
|---|---|
@timestamp | The reading's time; for a change of value, when its notification arrived; for an inventory's record, when its device was read |
ecs.version | 8.11.0 |
event.module | bacnet |
observer.name | The device's name in the console |
observer.hostname | The Director's host name |
observer.vendor, observer.product | VirtualMetric, BACnet |
network.protocol, network.transport | bacnet, udp |
labels.<name> | The dataset's labels, when it has any |
The records of an object and of a change of value dataset also name the BACnet device read:
| Field | Value |
|---|---|
server.address, server.port | The address the device was configured with, or the one its I-Am came from when it was found by its instance (its router's, behind one), and the port |
server.ip | The address, as an IP address |
bacnet.device.instance | The device's instance |
bacnet.device.address | Where it is reached (192.0.2.10:47808) |
bacnet.device.network, bacnet.device.mac | Behind a router, its network's number and its MAC, in hexadecimal with a colon between octets |
Object Records
event.dataset is bacnet.object, event.kind metric, event.category host, and event.type info.
| Field | Value |
|---|---|
bacnet.object.table | The dataset's table |
bacnet.object.<table>.<name>.<property> | The value of each property that was read of each object, the property by its name with underscores (present_value, status_flags, units; property_600 for a vendor's own) |
{
"@timestamp": "2026-10-14T12:00:00Z",
"ecs": { "version": "8.11.0" },
"event": { "module": "bacnet", "dataset": "bacnet.object", "kind": "metric", "category": ["host"], "type": ["info"] },
"observer": { "vendor": "VirtualMetric", "product": "BACnet", "name": "ahu-1", "hostname": "director01" },
"server": { "address": "192.0.2.10", "port": 47808, "ip": "192.0.2.10" },
"network": { "protocol": "bacnet", "transport": "udp" },
"labels": { "building": "north" },
"bacnet": {
"device": { "instance": 1234, "address": "192.0.2.10:47808" },
"object": {
"table": "ahu",
"ahu": {
"supply_temp": {
"present_value": 18.5,
"status_flags": { "in_alarm": false, "fault": false, "overridden": false, "out_of_service": false },
"units": "degrees-celsius"
},
"fan_status": { "present_value": true },
"fan_speed": { "present_value": 3 }
}
}
}
}
The values sit under the table's name and the object's, so a property keeps one type, and two tables can name an object alike. An object none of whose properties was read is absent from that record; a record with no object is not written.
Change of Value Records
event.dataset is bacnet.cov, event.kind metric, event.category host, and event.type info. One record per notification, and one per reading of an object that is read instead.
| Field | Value |
|---|---|
bacnet.cov.table | The dataset's table |
bacnet.cov.name | The object's name in the table |
bacnet.cov.object | The object (binary-value,7) |
bacnet.cov.source | notification, or poll for an object that is read |
bacnet.cov.confirmed | A notification's: whether the device waited for its acknowledgement |
bacnet.cov.time_remaining | A notification's: the seconds the device still keeps the subscription |
bacnet.cov.<table>.<name>.<property> | The values, as an object record's |
{
"@timestamp": "2026-10-14T12:03:41.512Z",
"ecs": { "version": "8.11.0" },
"event": { "module": "bacnet", "dataset": "bacnet.cov", "kind": "metric", "category": ["host"], "type": ["info"] },
"observer": { "vendor": "VirtualMetric", "product": "BACnet", "name": "ahu-1", "hostname": "director01" },
"server": { "address": "192.0.2.10", "port": 47808, "ip": "192.0.2.10" },
"network": { "protocol": "bacnet", "transport": "udp" },
"bacnet": {
"device": { "instance": 1234, "address": "192.0.2.10:47808" },
"cov": {
"table": "alarms",
"name": "filter_alarm",
"object": "binary-value,7",
"source": "notification",
"confirmed": true,
"time_remaining": 284,
"alarms": {
"filter_alarm": {
"present_value": true,
"status_flags": { "in_alarm": true, "fault": false, "overridden": false, "out_of_service": false }
}
}
}
}
}
Inventory Records
event.dataset is bacnet.inventory, event.kind state, and event.category host; event.type is info, or creation, change, or deletion with event.action added, changed, or removed. An inventory record has no server: the object may be any device's.
| Field | Value |
|---|---|
event.action | From the second sweep on: added, changed, unchanged, or removed |
bacnet.inventory.target | Whether the object's device is the one this BACnet device reads |
bacnet.inventory.device.instance | The device's instance |
bacnet.inventory.device.address | Where it is reached; with network and mac behind a router |
bacnet.inventory.device.name | Its Device object's name |
bacnet.inventory.device.vendor_name, vendor_id, model_name, firmware_revision, application_software_version, location, description | What it says of itself; what it does not say is left out |
bacnet.inventory.device.max_apdu_length, segmentation | The longest message it takes, and what it says of its segmentation (segmented-both, no-segmentation) |
bacnet.inventory.device.objects | How many objects it says it has |
bacnet.inventory.object.identifier | The object, as an object dataset's object is written (analog-input,1) |
bacnet.inventory.object.type, instance | Its type's name and its instance |
bacnet.inventory.object.name, description, units | Its name, description, and units, those it has |
error.type, error.message | incomplete and why, on the one record of a device whose objects could not be read |
{
"@timestamp": "2026-10-14T12:00:07Z",
"ecs": { "version": "8.11.0" },
"event": { "module": "bacnet", "dataset": "bacnet.inventory", "kind": "state", "category": ["host"], "type": ["change"], "action": "changed" },
"observer": { "vendor": "VirtualMetric", "product": "BACnet", "name": "ahu-1", "hostname": "director01" },
"network": { "protocol": "bacnet", "transport": "udp" },
"bacnet": {
"inventory": {
"target": true,
"device": {
"instance": 1234, "address": "192.0.2.10:47808", "name": "AHU-1", "vendor_name": "Example Controls",
"vendor_id": 260, "model_name": "AC-900", "firmware_revision": "4.2.1", "max_apdu_length": 1476,
"segmentation": "segmented-both", "objects": 153
},
"object": {
"identifier": "analog-input,1", "type": "analog-input", "instance": 1,
"name": "AHU-1 SAT", "description": "Supply air temperature", "units": "degrees-celsius"
}
}
}
}
Device State
The connection state follows the BACnet device:
- Connected while it answers any property of any of the device's datasets with its value, takes a subscription, or reports a change.
- Configuration error while it answers but refuses everything the datasets ask of it (objects it does not have), with the refusal and what it means; while the device at the address is not the instance named; and while two devices of the Director ask for one port on two addresses. A configuration that cannot run is a Configuration error too (see Configuration).
- Connection error while it is not found, does not answer, says it cannot answer now, its router has no route to it, or the Director's BBMD does not register the Director.
- Initialization error while Director cannot bind the port.
The state follows every dataset's latest reading, not only the datasets of the reading in progress. Each failure is said once in the device's log, again every 30 minutes for as long as it lasts, and its end once. A record that could not be written, or a failure in one reading, one notification, or one device of a sweep, affects that one alone and does not change the device's state.
Any change to the device or to its datasets restarts it: its subscriptions are cancelled and made again, and the comparison of its inventory starts anew. When the device stops, it cancels its subscriptions for three seconds at most; what it could not cancel, the BACnet device ends when the lifetime runs out, which is why a lifetime is never unlimited.
The BACnet device is not simulated. A dataset's records are the values of your own objects, and an inventory's the objects of a building, shapes a generated sample cannot stand in for.
What It Sends
Everything the device sends, so that "it only reads" can be checked against a capture:
| Message | When |
|---|---|
| Who-Is (unconfirmed) | To find a device by its instance, to ask a device at an address which it is when it does not answer for instance 4194303, and at the start and the half of an inventory's sweep |
| ReadProperty, ReadPropertyMultiple | Every reading |
| SubscribeCOV | To make, renew, and cancel a subscription |
SimpleACK, Error (unknown-subscription) | To a confirmed notification of a change of value |
Reject (unrecognized-service), Abort | To any other confirmed request it is sent |
| SegmentACK, Abort | While an answer comes in segments |
| Register-Foreign-Device (BVLL) | To its BBMD, when it names one |
It sends no WriteProperty, WritePropertyMultiple, AddListElement, RemoveListElement, CreateObject, DeleteObject, AtomicWriteFile, DeviceCommunicationControl, ReinitializeDevice, TimeSynchronization, AcknowledgeAlarm, LifeSafetyOperation, or private transfer, no I-Am, and nothing that writes or deletes an entry of a BBMD's tables. Director has no Device object of its own, and answers no Who-Is.
Not Supported
- BACnet/SC (TLS over WebSockets), BACnet/IPv6, and the data links a Director has no port for: MS/TP over a serial line, BACnet Ethernet, ARCNET, and LonTalk. Their devices are read through a BACnet router, as any routed device is.
- Writing of any kind (see What It Sends).
- Alarms and events: event notifications, GetEventInformation, GetAlarmSummary, and notification classes. An object's
event-stateandstatus-flagsare read as properties. - Trend logs (ReadRange), files (AtomicReadFile), schedules, and calendars, whose values are structures.
- SubscribeCOVProperty and the COV-multiple services: a subscription is to an object, and carries what the standard has it report.
- An element of an array as a dataset's property: a
priority-arrayis read whole. - A Device object of Director's own: Director answers no Who-Is and no ReadProperty, so it is in no workstation's device list. Unconfirmed notifications need none.
- Requests in segments: a request is cut to the device's longest message instead.
- Character strings in a double-byte set, and engineering units newer than the unit table (252, 254, and the 47808 range), which are written as their numbers.
- Network security (clause 24), which the standard has since removed.
Compatibility
The device follows ASHRAE 135 (BACnet), clauses 5.4, 6, 20 and 21, and Annex J (BACnet/IP). If one of your devices, routers or BBMDs answers differently from the standard, contact VirtualMetric support with its make and model.
Console
In the console, go to
-
General settings , as on every device. -
Connection settings :Addressing method chooses how the controller is named, and the step shows only that method's fields. The method is the console's own choice: Director never sees it, only the properties it fills.Address showsAddress ,Controller port (Optional) , andDevice instance (Optional) .Device instance showsDevice instance andNetwork number (Optional) , which keeps the Who-Is to one network.BACnet router showsRouter address ,Router port (Optional) ,Network number ,MAC address , andDevice instance (Optional) .
Port (47808) is on this step whichever method is chosen. -
Network settings :Foreign device registration , off by default. Off, the step showsBroadcast address (Optional) (255.255.255.255); on,BBMD address ,BBMD port , andRegistration lifetime (300). -
Advanced settings :Listen address (Optional) (0.0.0.0),APDU timeout (3),Maximum APDU retries (3),Segmentation ,Maximum segments (64, shown only whileSegmentation is on), andMaximum APDU length (1476). -
Data collection : the BACnet datasets the device reads. -
Review and create .
The device's page has the same tabs, and mac_address, or a network_number beside an address, is address alone is device_instance with no address is
The BACnet datasets are created in
BACnet Objects :Table name ,Interval (Optional) , theObjects table, the labels, and the pre-processing pipeline. Each object row is the object (asanalog-input,3), itsField (Optional) , the row'snamein Objects, and itsProperties (Optional) : the common ones chosen from a list, andOther properties (Optional) for any other, by its name or its number. An empty field is named by Director, and the table shows the name it gets, asanalog_input_3 (automatic). A row with no property readspresent-value.BACnet Changes of Value : the same settings and table, plusConfirmed notifications andSubscription lifetime (Optional) .BACnet Inventory :Sweep scope (Every controller on the network orOnly the controller the device reads ),Interval (Optional) , and, all optional,Discovery timeout ,Maximum controllers ,Minimum device instance ,Maximum device instance , andMaximum objects . WithOnly the controller the device reads , the fields that only a sweep of the network uses are not shown. A device reads one inventory: the data selection drawer refuses a second.
object,field,property;property (analog-input,3,supply_temp,present-value;status-flags), or the inventory dataset's records as the device page's
The console refuses two things that Director takes from a configuration file: a duration (read as whole seconds, as 5s or 1m) or an environment variable (${VAR}) in one of the device's number fields, and ${VAR} in a table name or an object. The dataset editors take an environment variable in any number field, and a duration in one that counts seconds.
Examples
By Address, with Objects and Changes of Value
Reading an air handling unit on the Director's own subnet by its address: its temperatures, fan, and valve every 30 seconds, and its filter and frost alarms as they change, with confirmed notifications... | |
By Instance, Through a BBMD, with a Network Inventory
Finding a chiller on another subnet by its instance, through that subnet's BBMD, reading its values every minute, and sweeping the instances 2000 to 2999 of the network every hour. This is the one device of the network the network inventory is assigned to... | |
Behind a BACnet Router
Reading a VAV box of an MS/TP line, station 12 on network 5, behind the BACnet router of floor 3. A slow line takes a longer timeout and answers that fit one message. Its inventory covers this device alone... | |
A Socket of Its Own
Reading the main energy meter at the BMS front end's address, as instance 3100, from a socket on one address of the Director and port 47809, for a host where another BACnet program holds 47808. The meter is still reached on 47808. On Linux a socket bound to one address hears no broadcast, which is why this device names its address... | |