Expose Home Assistant entities as BACnet objects on a local BACnet/IP device and import remote BACnet client points back into Home Assistant.
Built with bacpypes3.
Important
Breaking change in 2.0: the per-point Release button entities
(button.bacnet_doi_..._release) were removed and replaced by the
bacnet_hub.release service plus a bundled
tile feature.
How to migrate:
- Automations/scripts that pressed a release button now call the
bacnet_hub.releaseservice with the point's entity as target (optionalpriority, default8). - Dashboards: edit the point's Tile card and add BACnet: Release manual override under Features — see Setting it up on a tile.
- Orphaned button entries in the entity registry disappear after the next reload of the integration.
This integration has two roles:
- Home Assistant -> BACnet (local BACnet device)
- Publishes selected HA entities as BACnet objects.
- Keeps BACnet present values synchronized from HA state changes.
- Supports BACnet -> HA writeback for supported mappings.
- BACnet -> Home Assistant (remote BACnet clients)
- Discovers remote BACnet devices via
Who-Is/I-Am. - Imports supported remote points as HA entities.
- Uses BACnet COV subscriptions for event-driven updates.
- Labels-first auto mapping (no manual mapping UI).
- Automatic mapping lifecycle: add, refresh, remove, cleanup.
- Event-driven sync with debounce on registry/label/area changes.
- Deterministic entity IDs and stable unique IDs.
- Configurable BACnet write priority (8..16) per discovered client device.
bacnet_hub.releaseservice plus a bundled tile feature to relinquish the commanded value.- Built-in diagnostics for hub and discovered clients.
- Integration services:
bacnet_hub.reload,bacnet_hub.release.
- Home Assistant with custom integrations enabled.
- Network access to BACnet/IP segment.
- Dependency (from
manifest.json):bacpypes3==0.0.106
- HACS -> Integrations -> Custom repositories.
- Add this repository as type
Integration. - Install
BACnet Hub. - Restart Home Assistant.
- Settings -> Devices & Services -> Add Integration ->
BACnet Hub.
Copy custom_components/bacnet_hub to:
config/custom_components/bacnet_hub
Then restart Home Assistant.
Required fields:
instance(BACnet device instance,0..4194302)address(IPv4[/prefix][:port], example:192.168.31.36/24:47808)device_name(BACnet deviceobjectName)device_description(BACnet devicedescription)
Defaults:
device_name:HA-BACnet-Hubdevice_description:BACnet Hub - Home Assistant Custom Integration
Beyond the device fields above, the options flow offers:
debug_bacpypes(default: off) — escalates thebacpypes3loggers toDEBUGfor wire-level troubleshooting. Leave off in normal operation; the log volume is substantial.
The integration runs in labels mode and auto-manages mappings from selected labels.
- On setup/options, at least one label must be selected.
- A default label is auto-created if possible:
- Name:
BACnet - Icon:
mdi:server-network-outline - Color:
light-green
- Name:
- Entities can be discovered by direct entity label, by device label, or by labels assigned to the linked area (entity area or device area).
- Binary-like domains ->
binaryValue:binary_sensor,switch,light,lock,cover,input_boolean,alarm_control_panel,device_tracker,button
- Numeric/unit-based states ->
analogValue - Fallback ->
binaryValue
For climate.*, multiple BACnet mappings can be created:
hvac_modebinaryValuefor simpleoff/heat- otherwise
multiStateValuewith dynamicstateText
hvac_action->binaryValue(read-only mirror)current_temperature->analogValueset_temperature(reads HA attributetemperature) ->analogValue
analogValuebinaryValuemultiStateValue
Published mappings are mirrored as observer entities and split by platform:
sensor/binary_sensoronly (non-interactive read-only observers)- configuration-like observers use
entity_category=config(for example climate setpoints/modes)
Note: This avoids confusing UI behavior where toggles/sliders can be clicked but immediately snap back.
Write requests are accepted only if mapping/service checks pass; otherwise BACnet write is denied (writeAccessDenied).
Supported write targets:
light,switch,fan,group->turn_on/turn_offcover->open_cover/close_covernumber,input_number->set_valueclimate:- HVAC mode mapping ->
set_hvac_mode - setpoint mapping ->
set_temperature
- HVAC mode mapping ->
The integration discovers remote BACnet devices and imports supported points into HA.
analog-input(ai)analog-output(ao)analog-value(av)binary-input(bi)binary-output(bo)binary-value(bv)multi-state-value(mv)characterstring-value(csv)
ai->sensor(read-only)ao->numberif writable, elsesensorav->number(writable)bi->binary_sensor(read-only)bo->switchif writable, elsebinary_sensorbv->switch(writable)mv->select(writable)csv->text(writable)
Writable conditions:
ao/borequirepriorityArraysupport.av,bv,mv,csvare writable by design.
Implementation detail:
- Imported client point entities are created with
entity_registry_enabled_default = false(disabled by default until enabled in HA).
Commandable points (ao, bo, av, bv, mv with a priorityArray) are written at a configurable BACnet priority:
- Each discovered client device gets a Write priority
selectentity (options8..16, default8, category Configuration, disabled by default).8is Manual Operator — writes from HA override a controller's own program until the slot is released.16is the lowest priority and matches the behavior of a priority-less write.- The chosen priority survives restarts.
- Releasing a slot is done with the
bacnet_hub.releaseservice (see Services) or the bundled tile feature (see Lovelace tile feature).- Breaking change (2.0): the former per-point Release
buttonentities (button.bacnet_doi_<client>_<type>_<instance>_release) were removed. Automations or dashboards that pressed them must call the service (or use the tile feature) instead. Orphaned button entries in the entity registry are not deleted actively; they disappear after the next reload of the integration. - The release writes BACnet
Nullat the chosen priority, clearing that slot in the point'spriorityArrayso the remote controller takes over again with its own value. - Without a release, a value written from HA stays latched in the priority array indefinitely.
- Breaking change (2.0): the former per-point Release
- Writable client point entities expose extra state attributes:
priority_array: all 16 slots,nullfor free slots. Excluded from the recorder so the database is not flooded on every state change.relinquish_default: the value that applies when no slot is occupied.
- Initial auto-sync runs during setup.
- Mapping refresh is triggered by:
entity_registry_updateddevice_registry_updatedlabel_registry_updatedarea_registry_updated
- Debounce: 2 seconds.
- Stale/orphan published entities are automatically cleaned up.
Provides diagnostic sensors (examples):
- description
- firmware revision
- model name
- object identifier / object name
- system status
- vendor identifier / vendor name
- IP address / subnet mask / MAC address
For discovered clients, diagnostic sensors include similar device and network fields.
Published mirror entities use deterministic IDs based on hub instance + BACnet object instance:
sensor.bacnet_doi_<hub_instance>_av_<instance>binary_sensor.bacnet_doi_<hub_instance>_bv_<instance>
Client point entities:
<platform>.bacnet_doi_<client_instance>_<type_slug>_<object_instance>- Write priority:
select.bacnet_doi_<client_instance>_write_priority
Published unique IDs are stable and hub-scoped:
bacnet_hub:hub:<hub_key>:<object-type>:<instance>
Reload one BACnet Hub config entry.
Fields:
entry_id(optional)- If omitted and exactly one BACnet Hub entry exists, that entry is reloaded.
Release a priority array slot on one or more commandable client points (targeted by entity).
Fields:
priority(optional,1..16, default8= Manual Operator)- The slot that is cleared by writing BACnet
Null.
- The slot that is cleared by writing BACnet
Behavior:
- Only points that support
priorityArrayare accepted; other targets raise a validation error. - After the write the point is re-read immediately, so the HA state updates without waiting for the next COV notification.
- With multiple targets, errors are collected per entity and reported bundled at the end instead of aborting on the first failure.
Example:
service: bacnet_hub.release
target:
entity_id: number.bacnet_doi_1234_ao_1
data:
priority: 8The integration ships a tile feature (custom:bacnet-release-feature) and serves it automatically — no manual dashboard resource setup is needed. It replaces the removed release button entities and offers itself on every entity that exposes a priority_array attribute, i.e. the writable client points (number, switch, select) of commandable objects.
- Open the dashboard editor and edit (or add) a Tile card for the point, e.g.
switch.bacnet_doi_1234_bo_1. - Under Features click Add feature and pick BACnet: Release manual override (on a German frontend: BACnet: Handbedienung aufheben). The entry only shows up for entities that carry
priority_array; for sensors or published mirrors it is not offered. - Optionally set the
priority(1–16) in the card's YAML. It defaults to8and must match the slot your writes land in — with the default Write priority of8nothing needs to be changed. The feature has no visual editor beyond that single option.
type: tile
entity: switch.bacnet_doi_1234_bo_1
features:
- type: toggle
- type: custom:bacnet-release-feature
priority: 8- Full-width Release button (German frontend: Aufheben); only enabled while the configured slot is occupied, greyed out otherwise.
- Warning-colored while active — a manual override is a state the operator is supposed to notice.
- Releases immediately on click (no confirmation dialog) by calling
bacnet_hub.releasefor the tile's entity. - The module registers itself once HA's frontend is ready and repairs any tile that rendered before that; a one-line version stamp appears in the browser console on load, and
window.__bacnetReleaseFeature.heal()can be run from the console for support.
- BACnet/IP focus (
IPv4/prefix:portbind format). - Single config entry (
single_config_entry: true). - Labels-first auto model; legacy/manual mappings are removed during sync.
- Published
multiStateValuecurrently has no dedicated HA mirror platform entity. - BACnet sends no COV for
priorityArray/relinquishDefault; besides updating on writes/releases from HA and after COVpresentValuechanges, commandable points poll these properties every 30 seconds, so external changes appear with up to that much delay.
- No entities imported:
- Verify selected labels in options.
- Verify labels are attached to entity, its device, or the linked area.
- Address bind errors (
address already in use):- Ensure only one BACnet process binds the same IP/port.
- Direct BACnet writes rejected:
- Check write target is supported and corresponding HA service exists.
- Client points not updating:
- Confirm remote device supports COV/read for the point.
- Trigger reload via
bacnet_hub.reload.
Unit tests live in tests/ and run against the pinned dependencies:
pip install -r requirements_test.txt
pytestThe CI workflow (.github/workflows/validate.yml) runs manifest/translation validation, a Python syntax check, and the test suite on every PR.
MIT. See LICENSE.
Copyright (c) 2025-2026 Alessio Magliarella