A Home Assistant custom integration for discovering and controlling BACnet/IP devices on the local network.
Important
This plugin is a work-in-progress. YMMV!
- Device discovery via BACnet Who-Is broadcast, including devices behind JACE/MSTP routers
- Change of Value (COV) subscriptions for push-based updates with automatic polling fallback
- Writable points — control analog outputs, binary outputs, and multi-state outputs through Home Assistant
- Commandability detection — automatically distinguishes read-only vs. writable
*Valueobject types - Per-point configuration — rename, describe, hide/show, and set update mode (COV, poll, or auto) for each point
- 60+ BACnet unit mappings to Home Assistant device classes (temperature, pressure, power, energy, etc.)
| BACnet Object Type | HA Platform | Writable |
|---|---|---|
| Analog Input | Sensor | No |
| Analog Output | Number | Yes |
| Analog Value | Number or Sensor | If commandable |
| Binary Input | Binary Sensor | No |
| Binary Output | Switch | Yes |
| Binary Value | Switch or Binary Sensor | If commandable |
| Multi-State Input | Sensor (enum) | No |
| Multi-State Output | Select | Yes |
| Multi-State Value | Select or Sensor | If commandable |
- Python 3.11+
- Home Assistant 2024.1+
- Network access to BACnet/IP devices (UDP port 47808)
Copy the custom_components/bacnet/ directory into your Home Assistant custom_components/ folder:
<ha-config-dir>/
custom_components/
bacnet/
__init__.py
manifest.json
...
Then restart Home Assistant.
- Go to Settings > Devices & Services > Add Integration and search for BACnet.
- Select your local IP/subnet from the auto-detected dropdown (or enter it manually as CIDR, e.g.,
192.168.1.50/24). - Set the BACnet client device ID (default:
999). Optionally configure a BBMD address. - The integration will broadcast Who-Is and display discovered devices — select a device to add. Repeat the flow to add additional devices.
- If a device isn't discovered automatically, you can enter its address and device instance ID manually.
After setup, configure each device via Settings > Devices & Services > BACnet > Configure:
- Poll interval — how often to poll points not covered by COV (default: 30s, range: 5–3600s)
- Default update mode —
auto(COV with polling fallback),cov(COV only), orpoll(polling only) - COV lifetime — how long COV subscriptions last before renewal (default: 900s, range: 60–86400s)
- Configure Points — select a point to set its display name, description, exposure toggle, and update mode override
- Refresh — scan the device for new or removed points and update the point list
| Mode | Behavior |
|---|---|
auto |
Attempts COV subscription; silently falls back to polling if the device rejects it |
cov |
Requires COV — marks the entity unavailable if the subscription fails |
poll |
Polling only; never attempts COV |
- Singleton BAC0 client — only one BAC0 instance runs per system (UDP port 47808 constraint), managed with reference counting across multiple device entries
- Hybrid COV + polling coordinator — polls only points without active COV subscriptions; batches reads in groups of 25
- COV renewal — unconfirmed COV subscriptions are renewed at 80% of their lifetime
See doc/architecture.md for detailed design documentation.
# Install dependencies
poetry install
# Run tests
poetry run pytest
# Lint
poetry run ruff check .See LICENSE for details.