Wacnet (BACnet via Clojure)
Wacnet is a BACnet/IP driver that does not speak BACnet directly. Instead it talks to a small Wacnet service — a Java (Clojure) BACnet daemon — over a plain local REST API, and the service does the BACnet work on the wire. GEM sends it HTTP requests like "who is out there," "list this device's objects," "read this property," and the Wacnet service answers after querying the real BACnet devices.
Use Wacnet when the native BACnet integration can't run on a particular controller — most often because something else is already holding the BACnet/IP port (UDP 47808), or the host is a locked-down container that can't bind that port. The Wacnet service can bind it instead, and GEM reaches BACnet through the service. On a normal install where GEM can open the BACnet port itself, prefer the native BACnet driver — it is faster, richer, and is the one the BACnet Explorer works with.
GEM ⇄ Wacnet service (REST, usually on localhost) ⇄ your BACnet/IP devices (thermostats, controllers, meters). One GEM device row represents the connection to the Wacnet service. The BACnet devices themselves are discovered through the service, not added one-by-one in GEM.
This is a stripped-down bridge focused on discovery and raw property read/write. It is not listed in the BACnet Explorer interface picker (that tool targets native BACnet/IP devices), and it does not carry the native driver's thermostat verbs like setpoint, system_mode, or get_temperature. If you need those, use a native BACnet device.
Prerequisites
- A Java runtime on the GEM controller if you want GEM to launch the Wacnet service for you (see Manage Wacnet JVM below). Confirm with
java -versionon the host. GEM starts it with the bundledbin/wacnet/wacnet.jar. - Alternatively, a Wacnet service you run yourself, reachable over HTTP from the GEM host — in which case Java on the GEM host is not required.
- Your BACnet/IP devices reachable on the same network as the Wacnet service. The service (not GEM) binds the BACnet port and issues the Who-Is broadcasts, so it must sit on the BACnet subnet.
Setup steps
- Add the device. In the GEM admin console open Devices → Add Device and choose the driver Wacnet (BACnet via Clojure).
- Fill in the connection fields. All of them have working defaults, so a local, GEM-managed service needs almost nothing:
- Wacnet API Host — where the Wacnet service is running. Defaults to
127.0.0.1(localhost), which is right when GEM manages the service. - Wacnet API Port — the service's REST port. Defaults to
47800. - Manage Wacnet JVM — leave off if you run Wacnet yourself; turn it on to have GEM launch and supervise the bundled service (see below).
- Write Priority — the BACnet write priority used on writes,
1–16. Defaults to 14. Lower numbers win: a value written at priority 14 is overridden by anything already holding that point at 1–13.
- Wacnet API Host — where the Wacnet service is running. Defaults to
- Save and enable the device. On connect the driver points itself at
http://<host>:<port>/api/v1/bacnet/and, if Manage Wacnet JVM is on, starts the service. - Discover the BACnet network. Once connected, run the who_is command against the device (from a macro, or the device's command list) to make the service broadcast a Who-Is and enumerate the BACnet devices it can see.
- Explore a device's objects. Run get_objects with a device instance number to list that BACnet device's objects (analog inputs, binary values, etc.) so you know the object type/instance triples you'll read and write.
Manage Wacnet JVM
When Manage Wacnet JVM is on, GEM launches the bundled service as a child process (java -jar bin/wacnet/wacnet.jar) when the device connects, and automatically restarts it about 3 seconds after it exits. Keep a Java runtime installed on the controller for this to work — if java isn't on the path or the jar is missing, the service never comes up and the device stays offline.
When it's off, GEM assumes the Wacnet service is already running (started by you, another service manager, or a separate machine) and simply makes REST calls to the Wacnet API Host / Port you configured. Nothing is spawned or restarted by GEM in that mode.
Commands
Wacnet exposes four BACnet primitives. who_is and get_objects are the ones you run directly during commissioning; read_property and write_property are the low-level read/write the driver uses under the hood (including for any zone points you map).
| Command | Arguments | What it does |
|---|---|---|
who_is | — | Broadcasts a BACnet Who-Is through the service and returns the devices it discovers. Run this first. |
get_objects | device_instance | Lists the BACnet objects belonging to a device instance, so you can find object types and instance numbers. |
read_property | device_instance, object_type, object_instance, property | Reads one property (e.g. present-value) of a specific object. The service returns a {"present-value": …} shape, which the driver unwraps to the bare value. |
write_property | device_instance, object_type, object_instance, property, value, priority | Writes one property of an object at the given BACnet priority (defaults to the device's Write Priority). |
Wacnet writes use its native encoding: the driver sends a small BACnet write request of the form "set property X to value Y at priority N". If a write reports success but the device value doesn't change, another writer is almost certainly holding that point at a higher priority (a lower number) — raise Write Priority toward 1 and try again, or clear the higher priority at the source.
Mapping BACnet points to zones
Because Wacnet is built on GEM's BACnet foundation, a zone bound to a Wacnet device can be polled automatically — GEM re-reads the mapped points on an interval and keeps the zone's values current. To wire this up you give each zone an address (required for Wacnet zones) and tell the driver which BACnet object that address maps to.
The mapping lives in the device's Device Map attribute — a JSON object of zone address → { device_id, object_type, object_instance }. Set the address on the zone, add the matching entry to the Device Map, and the driver reads that object each poll cycle. This is the same point-mapping model used by native BACnet; the BACnet integration page covers the point-map fields and polling behavior in depth.
Place these zones under the subsystem that fits the underlying gear — Climate for thermostats and HVAC points is the usual choice. Wacnet's built-in subsystem hints don't match a stock GEM subsystem, so the zone editor won't pre-select one for you; pick it by hand (or create a dedicated building-automation subsystem).
Attribute reference
Device attributes
| Attribute | Label | Default | Notes |
|---|---|---|---|
ip | Wacnet API Host | 127.0.0.1 | Host running the Wacnet service. Usually localhost. |
port | Wacnet API Port | 47800 | REST port of the Wacnet service. |
start_wacnet | Manage Wacnet JVM | false | When on, GEM launches and restarts the bundled wacnet.jar. |
priority | Write Priority | 14 | BACnet write priority, 1–16 (1 = highest). |
Zone attributes
| Attribute | Purpose |
|---|---|
address | Required. A short key (e.g. office) that identifies the zone in the device's Device Map. |
Wacnet zones inherit the full BACnet point-map and polling attributes (read/write point triples, polling interval, deadband) from the BACnet foundation. See BACnet for the complete list and how the poll loop uses them.
Troubleshooting
wacnet error:repeats in the log and the device stays offline. GEM couldn't start or reach the service. If Manage Wacnet JVM is on, confirmjava -versionworks on the controller and thatbin/wacnet/wacnet.jarexists. If it's off, confirm the Wacnet service is actually running at the Host/Port you set.who_isreturns nothing. The Wacnet service has to be on the same network as the BACnet/IP devices and able to bind the BACnet port. A firewall between the service and the devices, or the service running on an isolated host, will produce an empty discovery.who_isworks but reads come back empty/undefined. Newer Wacnet builds wrap values as{"present-value": …}, which the driver unwraps; a much older build that returns values in a different shape can read as empty. Check the service version if reads are blank while discovery works.- A write reports success but the value doesn't change. BACnet priority — something is holding the point at a higher priority than your Write Priority (14 by default). Raise the priority (toward 1) and retry.
- The device won't appear in the BACnet Explorer. Expected — the Explorer targets native BACnet/IP devices, not Wacnet. Drive Wacnet through its own commands and zone mappings instead.
Related documentation
- BACnet — the native BACnet integration and Explorer; the preferred path when the host can bind the BACnet port, and the reference for the shared point-map/polling model.
- Devices — adding and configuring the Wacnet device.
- Zones — where mapped BACnet points surface as controllable/monitored zones.
- Subsystems — choosing where Wacnet zones live (e.g. Climate).
- Commands — running
who_is,get_objects, and property reads/writes.