Controller BACnet/IP Server
The Aida Controller BACnet/IP Server exposes Inspextor lighting zones to a municipal or commercial Building Management System (BMS) over BACnet/IP (UDP port 47808).
Each controller acts as a single BACnet device that aggregates many PoE nodes into cluster-level control and sensor points — similar to how the Aida Home Hub Matter bridge exposes zone-level controls.
:::info Parallel integration paths
| Path | Protocol | Typical use |
|---|---|---|
| BACnet/IP Server (this doc) | BACnet/IP | BMS graphics, schedules, municipal submittals |
| Controller Public API | HTTPS REST | Scripts, legacy 1.0 clients, non-BACnet integrators |
| Aida Platform API | Cloud HTTPS | Multi-site management — not per-controller BACnet |
PoE nodes use CoAP/MQTT at runtime; they never speak BACnet directly. AIDA is always the northbound gateway.
Municipal submittals: Municipal BMS Integration Brief · Level 1 PICS Addendum · Commissioning Checklist :::
Integration levels
| Level 1 (available today) | Level 2 (planned) | |
|---|---|---|
| State source | Controller-native: health-check poll, INX events, command echo → device_states | Authoritative state from Realtime Controller (RTC) |
| BMS reads | ReadProperty on cached presentValue (poll interval, default 30 s) | ReadProperty + SubscribeCOV |
| BMS writes | WriteProperty → same INX commands as Public API | Same + priority array, BMS override, demand response |
| Sensors | Occupancy, lux, temperature, sound (when present) | Adds CO₂, humidity, zone status |
| RTC required | No | Yes |
Default deployment: Level 1 on all Controller 2.0 sites. Choose Level 2 when the Owner requires COV, CO₂-driven DCV, or full municipal commissioning per project PICS.
:::warning Level 1 limitations SubscribeCOV, CO₂/humidity BACnet points, scene MSO, BMS override, and demand-response BACnet objects are Level 2 only. See Municipal BMS Integration Brief — Level 1 limitations. :::
Architecture (Level 1)
State is eventually consistent: updated when devices are polled online, when INX events arrive (mot/vac, lux/temp/sound sentype), and when commands succeed (BACnet write, Public API, or UI).
Enabling the server
The BACnet/IP server is a controller module (bacnet-server):
- Log in to the controller as SAdmin (or a role with module management).
- Open Modules and enable BACnet/IP Server.
- Configure module settings (device instance, UDP bind, exported clusters) — see Configuration.
- Ensure UDP 47808 is open on the controller VLAN (firewall / switch ACLs).
The server starts when the module is enabled and stops when disabled.
BACnet device identity
| Property | Default | Notes |
|---|---|---|
| Device instance | 389001 | Must be unique on the BACnet network |
| Device name | AIDA-CTRL-01 | Configurable |
| Vendor name | MHT Technologies | |
| Model name | Inspextor AIDA Controller | |
| Firmware revision | Controller software version | Automatic |
The controller responds to Who-Is with I-Am when the module is running.
Level 1 object model
Objects are allocated in instance blocks per cluster (default block size 100):
base = legacy_cluster_id × instance_block_size # e.g. cluster legacy id 2 → base 200
| Object | BACnet type | Instance | Name pattern | BMS read | BMS write |
|---|---|---|---|---|---|
| Zone power | binary-output | base + 1 | {cluster}-PWR | Yes | Yes |
| Zone dim | analog-output | base + 2 | {cluster}-DIM | Yes | Yes (0–100 %) |
| Zone CCT | analog-output | base + 3 | {cluster}-CCT | Yes¹ | Yes (3000–5000 K) |
| Occupancy | binary-input | base + 10 | {cluster}-OCC | Yes | No |
| Illuminance | analog-input | base + 11 | {cluster}-LUX | Yes² | No |
| Temperature | analog-input | base + 13 | {cluster}-TEMP | Yes² | No |
| Sound | analog-input | base + 15 | {cluster}-SND | Yes² | No |
¹ Marked out-of-service if the cluster has no tunable-white fixtures reporting CCT.
² Marked out-of-service if no sensor of that type exists in the cluster.
Not in Level 1: scene MSO, CO₂, humidity, SubscribeCOV, BMS override / demand-response points.
Aggregation rules
When the BMS reads a cluster point, values are aggregated from all devices in that cluster:
| Point | Rule |
|---|---|
| Dim | Mean of online lighting devices (dim / pp); prefers keyw_cluster_match devices |
| Power | true if any device is on or dim > 0 |
| CCT | Mean of devices reporting color temperature |
| Occupancy | true if any device reports occupied (last mot/vac/on/off event) |
| Lux | Max across sensors |
| Temperature | Mean across sensors |
| Sound | Max across sensors |
When all contributing devices are offline or data is older than the staleness threshold, objects set reliability=no-confidence.
Command translation (writes)
BACnet WriteProperty on writable objects dispatches the same INX events as the Public API:
| BACnet point | INX event | Example |
|---|---|---|
| Power ON/OFF | on / off | {"e": {"on": "Lobby"}} |
| Dim | fl | {"e": {"fl": "Lobby,1,75"}} |
| CCT | attune | {"e": {"attune": "Lobby,4000"}} |
All writes pass through the controller ExecutionGateway to PoE nodes.
:::note RGBW and shades RGBW and shade control are available via the Public API today. BACnet write mapping for RGBW and shade objects is planned for a later phase. :::
Discovery and commissioning (Level 1)
- Enable the BACnet/IP Server module on the controller.
- From the BMS subnet, send Who-Is (or browse by IP) — confirm I-Am from the configured device instance.
- Export the object inventory:
GET /api/v1/bacnet/inventory(authenticated) orGET /api/v1/bacnet/pics.md. - Write dim/power on
{cluster}-DIM/{cluster}-PWR— confirm zone lighting responds. - Read dim — value updates within one state refresh cycle (default 30 s) when devices are online.
- Trigger occupancy or sentype events in a zone — confirm
{cluster}-OCC,{cluster}-LUX, etc. - Document in submittal PICS that reads are last-known controller state (poll + event memory), not a certified live sensor stream.
- Confirm SubscribeCOV is not advertised (Level 1).
REST helpers (controller UI API)
These endpoints help commissioning and submittals (require controller JWT, not BACnet):
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/bacnet/status | Module health, bind address, object count, last sync |
| GET | /api/v1/bacnet/inventory | JSON object list (cluster, instance, name, read/write) |
| GET | /api/v1/bacnet/pics | PICS JSON (Level 1 services and object types) |
| GET | /api/v1/bacnet/pics.md | PICS markdown for submittal packages |
Example (after POST /api/v1/auth/login):
curl -s "https://192.168.1.77/api/v1/bacnet/inventory" \
-H "Authorization: Bearer $ACCESS_TOKEN" | jq '.objectCount'
Configuration
Module config is stored under the bacnet-server module entry (JSON in modules.config):
{
"device_instance": 389001,
"device_name": "AIDA-CTRL-01",
"integration_level": 1,
"udp_port": 47808,
"bind_address": "0.0.0.0",
"state_refresh_sec": 30,
"instance_block_size": 100,
"exported_cluster_ids": [],
"health_check_interval_sec": 300,
"stale_multiplier": 3
}
| Setting | Description |
|---|---|
device_instance | BACnet device object instance (unique on network) |
exported_cluster_ids | Empty = all clusters with devices; or list of cluster UUIDs |
state_refresh_sec | How often BACnet presentValue is refreshed from device_states |
instance_block_size | Spacing between cluster instance blocks |
Per-cluster overrides in clusters.metadata.bacnet:
{
"export": true,
"base_instance": 500,
"name_prefix": "North-Wing-Lobby"
}
Set "export": false to exclude a cluster from BACnet export.
Network requirements
- Port: UDP 47808 on the controller management/lighting VLAN.
- Discovery: BMS Who-Is on the same subnet, or directed to the controller IP.
- Cross-subnet: BACnet/IP router or BBMD (future phase).
Choosing Level 1 vs Level 2
| Owner requirement | Level |
|---|---|
| BMS commands dim / on / off / CCT | 1 |
| BMS graphics show occupancy or daylight (poll-based) | 1 (with PICS caveats) |
| Demand-controlled ventilation from CO₂ | 2 |
| SubscribeCOV / near-real-time lux step response | 2 |
| Full Oakland County–style PICS with override / DR | 2 |
Document the selected level in project submittals.
Implementation status (Controller 2.0)
| Component | Status |
|---|---|
Level 1 state pipeline (cluster_state: poll, INX ingest, command echo) | Implemented |
| BACnet/IP server module (bacpypes3, Read/WriteProperty, I-Am) | Implemented |
| Level 1 point export and aggregation | Implemented |
| PICS / inventory REST export | Implemented |
| Public API read of occupancy/lux/temp/sound | Partial (dim/CCT on routes; full sensor fields pending) |
Passive SENSOR-STATUS-REQUEST ingest | Pending |
| Level 2 + Realtime Controller mirror + COV | Planned |
| RGBW / shade BACnet objects | Planned |
Related documentation
- Municipal BMS Integration Brief — FAQ, protocol stack, BMS vs standalone, Level 2 roadmap
- Level 1 PICS Addendum — Writable vs read-only submittal template
- BACnet Commissioning Checklist — Level 1 and Level 2 acceptance tests
- Controller Public API — REST integration on
/aida/api/*
Vendor: MHT Technologies · Product: Inspextor AIDA Controller 2.0