Server and Application

BACnet Application

BACnet application layer orchestrator per ASHRAE 135-2016.

class bac_py.app.application.DeviceInfo(max_apdu_length, segmentation_supported)[source]

Bases: object

Cached peer device capabilities from I-Am responses (Clause 19.4).

Parameters:
  • max_apdu_length (int)

  • segmentation_supported (int)

max_apdu_length: int

Maximum APDU length accepted by the peer device.

segmentation_supported: int

Segmentation support level (Segmentation enum value).

class bac_py.app.application.BBMDConfig(bdt_entries=<factory>)[source]

Bases: object

Configuration for BBMD on a router port.

Parameters:

bdt_entries (list[BDTEntry])

bdt_entries: list[BDTEntry]

Initial Broadcast Distribution Table entries (including self).

If empty, the BBMD starts with an empty BDT (foreign-device-only mode).

class bac_py.app.application.RouterPortConfig(port_id, network_number, interface='0.0.0.0', port=47808, broadcast_address='255.255.255.255', bbmd_config=None, ipv6=False, multicast_address='', vmac=None, sc_config=None, ethernet_interface=None, ethernet_mac=None)[source]

Bases: object

Configuration for a single router port.

Parameters:
port_id: int
network_number: int
interface: str = '0.0.0.0'
port: int = 47808
broadcast_address: str = '255.255.255.255'

Directed broadcast address for this port’s subnet.

bbmd_config: BBMDConfig | None = None
ipv6: bool = False

Use BACnet/IPv6 (Annex U) transport for this port.

multicast_address: str = ''

IPv6 multicast group address. Defaults to ff02::bac0.

vmac: bytes | None = None

3-byte VMAC for IPv6. Auto-generated if None.

sc_config: SCTransportConfig | None = None

BACnet/SC transport for this router port. Mutually exclusive with ipv6.

ethernet_interface: str | None = None

Network interface for BACnet Ethernet transport on this router port.

ethernet_mac: bytes | None = None

Explicit 6-byte MAC for Ethernet transport on this router port.

class bac_py.app.application.RouterConfig(ports=<factory>, application_port_id=1)[source]

Bases: object

Configuration for a router device.

Parameters:
ports: list[RouterPortConfig]
application_port_id: int = 1
class bac_py.app.application.DeviceConfig(instance_number, name='bac-py', vendor_name='bac-py', vendor_id=0, model_name='bac-py', firmware_revision='', application_software_version='', interface='0.0.0.0', port=47808, apdu_timeout=6000, apdu_segment_timeout=2000, apdu_retries=3, max_apdu_length=1476, max_segments=None, router_config=None, broadcast_address='255.255.255.255', password=None, ipv6=False, multicast_address='', vmac=None, sc_config=None, ethernet_interface=None, ethernet_mac=None)[source]

Bases: object

Configuration for a BACnet device.

Parameters:
  • instance_number (int)

  • name (str)

  • vendor_name (str)

  • vendor_id (int)

  • model_name (str)

  • firmware_revision (str)

  • application_software_version (str)

  • interface (str)

  • port (int)

  • apdu_timeout (int)

  • apdu_segment_timeout (int)

  • apdu_retries (int)

  • max_apdu_length (int)

  • max_segments (int | None)

  • router_config (RouterConfig | None)

  • broadcast_address (str)

  • password (str | None)

  • ipv6 (bool)

  • multicast_address (str)

  • vmac (bytes | None)

  • sc_config (SCTransportConfig | None)

  • ethernet_interface (str | None)

  • ethernet_mac (bytes | None)

instance_number: int

BACnet device instance number (0-4194302).

name: str = 'bac-py'

Device object name.

vendor_name: str = 'bac-py'

Vendor name string.

vendor_id: int = 0

ASHRAE-assigned vendor identifier.

model_name: str = 'bac-py'

Device model name string.

firmware_revision: str = ''

Firmware revision string. Defaults to the bac-py package version.

application_software_version: str = ''

Application software version string. Defaults to the bac-py package version.

interface: str = '0.0.0.0'

Local IP address to bind to ("0.0.0.0" for all).

port: int = 47808

UDP port number (default 0xBAC0 / 47808).

apdu_timeout: int = 6000

APDU timeout in milliseconds.

apdu_segment_timeout: int = 2000

Segment timeout in milliseconds.

apdu_retries: int = 3

Maximum number of APDU retries.

max_apdu_length: int = 1476

Maximum APDU length in bytes.

max_segments: int | None = None

Maximum segments accepted, or None for unlimited.

router_config: RouterConfig | None = None

Optional router configuration for multi-network mode.

broadcast_address: str = '255.255.255.255'

Directed broadcast address for this subnet. Default global broadcast works on physical networks; set to the subnet broadcast (e.g. "192.168.1.255") when running in Docker bridge networks where global broadcast is not routable.

password: str | None = None

Optional password for DeviceCommunicationControl and ReinitializeDevice services (1-20 characters, per Clause 16.1.3.1 and 16.4.3.4). When set, incoming requests must include a matching password.

ipv6: bool = False

Use BACnet/IPv6 (Annex U) transport instead of BACnet/IP (Annex J).

multicast_address: str = ''

IPv6 multicast group address. Defaults to ff02::bac0 when ipv6 is True. Ignored for IPv4 mode.

vmac: bytes | None = None

3-byte VMAC for IPv6 transport. Auto-generated if None. Ignored for IPv4 mode.

sc_config: SCTransportConfig | None = None

BACnet/SC transport configuration. When set, uses SC transport instead of BIP/BIP6. Mutually exclusive with ipv6.

ethernet_interface: str | None = None

Network interface for BACnet Ethernet (Clause 7) transport, e.g. "eth0". When set, uses raw 802.3/802.2 Ethernet instead of BIP/BIP6. Mutually exclusive with ipv6 and sc_config.

ethernet_mac: bytes | None = None

Explicit 6-byte MAC for Ethernet transport. Auto-detected from the interface if None. Required on macOS where auto-detection is not supported.

class bac_py.app.application.ForeignDeviceStatus(bbmd_address, ttl, is_registered, last_result)[source]

Bases: object

Foreign device registration status.

Provides a snapshot of the current registration state when operating as a foreign device via a BBMD.

Parameters:
  • bbmd_address (str)

  • ttl (int)

  • is_registered (bool)

  • last_result (str | None)

bbmd_address: str

BBMD address string (e.g. "192.168.1.1:47808").

ttl: int

Registration time-to-live in seconds.

is_registered: bool

Whether registration is currently active.

last_result: str | None

Last BVLC result code name, or None if no response yet.

class bac_py.app.application.BACnetApplication(config)[source]

Bases: object

Central orchestrator connecting all protocol layers.

Wires transport, network, TSMs, and service dispatch.

Parameters:

config (DeviceConfig)

property object_db: ObjectDatabase

The object database for this device.

property service_registry: ServiceRegistry

The service handler registry.

property config: DeviceConfig

The device configuration.

property cov_manager: COVManager | None

The COV subscription manager, or None if not started.

property event_engine: EventEngine | None

The event/alarm evaluation engine, or None if not started.

property dcc_state: EnableDisable

The current DeviceCommunicationControl state.

set_dcc_state(state, duration=None)[source]

Set the DeviceCommunicationControl state.

Parameters:
  • state (EnableDisable) – New DCC state (ENABLE, DISABLE, or DISABLE_INITIATION).

  • duration (int | None (default: None)) – Optional duration in minutes. When provided and state is not ENABLE, a timer is set to auto-re-enable after the specified number of minutes.

Return type:

None

property device_object_identifier: Any

The device object identifier for this application.

Returns the ObjectIdentifier of the device object from the object database. Used by the COV manager to populate the initiating device identifier in notifications.

get_device_info(address)[source]

Look up cached peer device capabilities.

Returns cached DeviceInfo from I-Am responses, or None if no information is available for address.

Parameters:

address (BACnetAddress) – The peer device address.

Return type:

DeviceInfo | None

Returns:

Cached device info, or None.

async start()[source]

Start the transport and initialize all layers.

Return type:

None

async stop()[source]

Stop the application and clean up resources.

This method is idempotent – calling it multiple times is safe.

Return type:

None

async run()[source]

Start the application and block until stopped.

Return type:

None

async register_as_foreign_device(bbmd_address, ttl=60)[source]

Register as a foreign device with a BBMD.

Attaches a foreign device manager to the primary transport and begins periodic re-registration at TTL/2 intervals.

Parameters:
  • bbmd_address (str) – Address of the BBMD. For IPv4 use "192.168.1.1" or "192.168.1.1:47808". For IPv6 use "[fd00::1]:47808" or "fd00::1".

  • ttl (int (default: 60)) – Registration time-to-live in seconds.

Raises:

RuntimeError – If already registered, application not started, or running in router mode.

Return type:

None

async deregister_foreign_device()[source]

Deregister from the BBMD and stop re-registration.

Sends a Delete-Foreign-Device-Table-Entry to the BBMD so the entry is removed immediately rather than waiting for TTL expiry.

Raises:

RuntimeError – If not registered as a foreign device.

Return type:

None

property is_foreign_device: bool

Whether this device is currently registered as a foreign device.

property foreign_device_status: ForeignDeviceStatus | None

Current foreign device registration status.

Returns None if foreign device mode is not active.

async wait_for_registration(timeout=10.0)[source]

Wait for foreign device registration to complete.

Blocks until the BBMD confirms registration or the timeout elapses. Useful after register_as_foreign_device() to ensure broadcasts will be distributed before performing discovery.

Parameters:

timeout (float (default: 10.0)) – Maximum seconds to wait.

Return type:

bool

Returns:

True if registered, False if timeout expired.

send_network_message(message_type, data, destination=None)[source]

Send a network-layer message (non-APDU).

Parameters:
  • message_type (int) – Network message type code.

  • data (bytes) – Encoded message payload.

  • destination (BACnetAddress | None (default: None)) – Target address. If None, broadcasts locally.

Raises:

RuntimeError – If the application is not started or is running in router mode (which has its own message API).

Return type:

None

add_route(network, router_address)[source]

Pre-populate the router cache for a remote network.

Allows sending to a remote network without broadcast-based router discovery. Useful in Docker or test environments where broadcast delivery is unreliable.

Parameters:
  • network (int) – The remote network number.

  • router_address (str) – IP address of the router (e.g. "172.30.1.150" or "172.30.1.150:47808").

Raises:

RuntimeError – If the application is not started.

Return type:

None

register_network_message_handler(message_type, handler)[source]

Register a handler for incoming network-layer messages.

Parameters:
  • message_type (int) – Network message type code to listen for.

  • handler (Callable[..., None]) – Called with (decoded_message, source_mac) when a matching network message is received.

Raises:

RuntimeError – If the network layer is not available.

Return type:

None

unregister_network_message_handler(message_type, handler)[source]

Remove a network-layer message handler.

Parameters:
  • message_type (int) – Network message type code.

  • handler (Callable[..., None]) – The handler to remove.

Return type:

None

async confirmed_request(destination, service_choice, service_data, timeout=None)[source]

Send a confirmed request and await response.

Parameters:
  • destination (BACnetAddress) – Target device address.

  • service_choice (int) – Confirmed service choice number.

  • service_data (bytes) – Encoded service request bytes.

  • timeout (float | None (default: None)) – Optional caller-level timeout in seconds. When provided, the request is cancelled if no response is received within this duration (raises asyncio.TimeoutError). When None, the TSM’s built-in retry/timeout logic is used exclusively.

Return type:

bytes

Returns:

ComplexACK service data, or empty bytes for SimpleACK.

unconfirmed_request(destination, service_choice, service_data)[source]

Send an unconfirmed request.

Parameters:
  • destination (BACnetAddress) – Target device address.

  • service_choice (int) – Unconfirmed service choice number.

  • service_data (bytes) – Encoded service request bytes.

Return type:

None

send_confirmed_cov_notification(service_data, destination, service_choice)[source]

Send a confirmed COV notification (fire-and-forget).

Unlike confirmed_request, this does not await a response. COV notifications are best-effort; failures are logged but do not propagate.

Parameters:
  • service_data (bytes) – Encoded service request bytes.

  • destination (BACnetAddress) – Target device address.

  • service_choice (int) – Confirmed service choice number.

Return type:

None

register_cov_callback(process_id, callback)[source]

Register a callback for incoming COV notifications.

Parameters:
  • process_id (int) – Subscriber process identifier to match.

  • callback (Callable[..., Any]) – Called with (notification, source) when a COV notification arrives for this process ID.

Return type:

None

unregister_cov_callback(process_id)[source]

Remove a COV notification callback.

Return type:

None

Parameters:

process_id (int)

register_temporary_handler(service_choice, handler)[source]

Register a temporary listener for unconfirmed service responses.

Return type:

None

Parameters:
unregister_temporary_handler(service_choice, handler)[source]

Remove a temporary listener.

Return type:

None

Parameters:

Server

Default BACnet server handlers per ASHRAE 135-2016.

Provides handlers for Who-Is, ReadProperty, WriteProperty, ReadPropertyMultiple, WritePropertyMultiple, and ReadRange that work with the local ObjectDatabase and DeviceObject.

class bac_py.app.server.DefaultServerHandlers(app, object_db, device)[source]

Bases: object

Standard BACnet service handlers for a server device.

Registers handlers for ReadProperty, WriteProperty, Who-Is, ReadPropertyMultiple, WritePropertyMultiple, and ReadRange with the application’s service registry.

Parameters:
register()[source]

Register all default handlers with the application.

Return type:

None

async handle_read_property(service_choice, data, source)[source]

Handle ReadProperty-Request per Clause 15.5.

Decodes the request, looks up the object and property in the database, and returns the encoded ReadPropertyACK.

Parameters:
  • service_choice (int) – Confirmed service choice code.

  • data (bytes) – Raw service request bytes.

  • source (BACnetAddress) – Address of the requesting device.

Return type:

bytes

Returns:

Encoded ReadProperty-ACK service data.

Raises:

BACnetError – If the object or property is not found.

async handle_write_property(service_choice, data, source)[source]

Handle WriteProperty-Request per Clause 15.9.

Decodes the request, looks up the object in the database, and writes the property value. Returns None for SimpleACK.

Parameters:
  • service_choice (int) – Confirmed service choice code.

  • data (bytes) – Raw service request bytes.

  • source (BACnetAddress) – Address of the requesting device.

Return type:

bytes | None

Returns:

None (SimpleACK response).

Raises:

BACnetError – If the object or property is not found, or the write is not permitted.

async handle_subscribe_cov(service_choice, data, source)[source]

Handle SubscribeCOV-Request per Clause 13.14.

Returns None (SimpleACK) on success.

Parameters:
  • service_choice (int) – Confirmed service choice code.

  • data (bytes) – Raw service request bytes.

  • source (BACnetAddress) – Address of the requesting device.

Raises:

BACnetError – If the monitored object does not exist or COV is not supported.

Return type:

bytes | None

async handle_subscribe_cov_property(service_choice, data, source)[source]

Handle SubscribeCOVProperty-Request per Clause 13.15.

Parameters:
  • service_choice (int) – Confirmed service choice code.

  • data (bytes) – Raw service request bytes.

  • source (BACnetAddress) – Address of the requesting device.

Return type:

bytes | None

async handle_subscribe_cov_property_multiple(service_choice, data, source)[source]

Handle SubscribeCOVPropertyMultiple-Request per Clause 13.16.

Parameters:
  • service_choice (int) – Confirmed service choice code.

  • data (bytes) – Raw service request bytes.

  • source (BACnetAddress) – Address of the requesting device.

Return type:

bytes | None

async handle_confirmed_cov_notification_multiple(service_choice, data, source)[source]

Handle ConfirmedCOVNotification-Multiple per Clause 13.17.

Parameters:
  • service_choice (int) – Confirmed service choice code.

  • data (bytes) – Raw service request bytes.

  • source (BACnetAddress) – Address of the requesting device.

Return type:

bytes | None

async handle_unconfirmed_cov_notification_multiple(service_choice, data, source)[source]

Handle UnconfirmedCOVNotification-Multiple per Clause 13.18.

Parameters:
  • service_choice (int) – Unconfirmed service choice code.

  • data (bytes) – Raw service request bytes.

  • source (BACnetAddress) – Address of the sending device.

Return type:

None

async handle_who_is(service_choice, data, source)[source]

Handle Who-Is-Request per Clause 16.10.

Checks if the local device instance is within the requested range and responds with an I-Am if so.

Parameters:
  • service_choice (int) – Unconfirmed service choice code.

  • data (bytes) – Raw service request bytes.

  • source (BACnetAddress) – Address of the requesting device.

Return type:

None

async handle_read_property_multiple(service_choice, data, source)[source]

Handle ReadPropertyMultiple-Request per Clause 15.7.

Decodes the request, reads all requested properties from the database, and returns the encoded ReadPropertyMultiple-ACK. Per-property errors are embedded in the ACK (no top-level error). Supports ALL, REQUIRED, and OPTIONAL special property identifiers per Clause 15.7.3.2.

Parameters:
  • service_choice (int) – Confirmed service choice code.

  • data (bytes) – Raw service request bytes.

  • source (BACnetAddress) – Address of the requesting device.

Return type:

bytes

Returns:

Encoded ReadPropertyMultiple-ACK service data.

async handle_write_property_multiple(service_choice, data, source)[source]

Handle WritePropertyMultiple-Request per Clause 15.10.

Uses a two-pass approach for atomicity: validates all writes first, then applies them. Either all writes succeed or none are applied.

Parameters:
  • service_choice (int) – Confirmed service choice code.

  • data (bytes) – Raw service request bytes.

  • source (BACnetAddress) – Address of the requesting device.

Return type:

bytes | None

Returns:

None (SimpleACK response) on full success.

Raises:

BACnetError – On first validation or write failure.

async handle_read_range(service_choice, data, source)[source]

Handle ReadRange-Request per Clause 15.8.

Decodes the request, reads the list/array property, applies the range qualifier, and returns the encoded ReadRange-ACK.

Return type:

bytes

Returns:

Encoded ReadRange-ACK service data.

Raises:

BACnetError – If the object or property is not found.

Parameters:
async handle_device_communication_control(service_choice, data, source)[source]

Handle DeviceCommunicationControl-Request per Clause 16.1.

Sets the application DCC state and optionally starts a timer to auto-re-enable after the specified duration.

Return type:

bytes | None

Returns:

None (SimpleACK response).

Raises:

BACnetError – If the password does not match (Clause 16.1.3.1).

Parameters:
async handle_reinitialize_device(service_choice, data, source)[source]

Handle ReinitializeDevice-Request per Clause 16.4 & 19.1.

Manages backup/restore state transitions on the Device object per Clause 19.1:

  • START_BACKUPsystem_status=BACKUP_IN_PROGRESS

  • END_BACKUPsystem_status=OPERATIONAL

  • START_RESTOREsystem_status=DOWNLOAD_IN_PROGRESS

  • END_RESTOREsystem_status=OPERATIONAL

  • ABORT_RESTOREsystem_status=OPERATIONAL

Return type:

bytes | None

Returns:

None (SimpleACK response).

Raises:

BACnetError – If the password does not match or the state transition is invalid.

Parameters:
async handle_time_synchronization(service_choice, data, source)[source]

Handle TimeSynchronization-Request per Clause 16.7.

Logs the received time. Does not update the local clock.

Return type:

None

Parameters:
async handle_utc_time_synchronization(service_choice, data, source)[source]

Handle UTCTimeSynchronization-Request per Clause 16.8.

Logs the received UTC time. Does not update the local clock.

Return type:

None

Parameters:
async handle_atomic_read_file(service_choice, data, source)[source]

Handle AtomicReadFile-Request per Clause 14.1.

Return type:

bytes

Returns:

Encoded AtomicReadFile-ACK service data.

Raises:

BACnetError – If the object is not found or is not a FileObject.

Parameters:
async handle_atomic_write_file(service_choice, data, source)[source]

Handle AtomicWriteFile-Request per Clause 14.2.

Return type:

bytes

Returns:

Encoded AtomicWriteFile-ACK service data.

Raises:

BACnetError – If the object is not found or is not a FileObject.

Parameters:
async handle_create_object(service_choice, data, source)[source]

Handle CreateObject-Request per Clause 15.3.

Creates a new object in the database and returns the encoded object identifier.

Return type:

bytes

Returns:

Encoded APPLICATION-tagged ObjectIdentifier.

Raises:

BACnetError – If the object type is unsupported or the object identifier already exists.

Parameters:
async handle_delete_object(service_choice, data, source)[source]

Handle DeleteObject-Request per Clause 15.4.

Removes the object from the database.

Return type:

bytes | None

Returns:

None (SimpleACK response).

Raises:

BACnetError – If the object does not exist or is a Device object.

Parameters:
async handle_add_list_element(service_choice, data, source)[source]

Handle AddListElement-Request per Clause 15.1.

Decodes the elements from the request, validates that the target property is a list, checks write access, and extends the list.

Return type:

bytes | None

Returns:

None (SimpleACK response).

Raises:

BACnetError – If the object or property is not found, or the property is not a list or is read-only.

Parameters:
async handle_remove_list_element(service_choice, data, source)[source]

Handle RemoveListElement-Request per Clause 15.2.

Decodes the elements from the request, validates that the target property is a list, checks write access, and removes matching entries. Non-matching elements are silently ignored per the standard.

Return type:

bytes | None

Returns:

None (SimpleACK response).

Raises:

BACnetError – If the object or property is not found, or the property is not a list or is read-only.

Parameters:
async handle_acknowledge_alarm(service_choice, data, source)[source]

Handle AcknowledgeAlarm-Request per Clause 13.5.

Updates the acked_transitions property on the target object to mark the appropriate transition as acknowledged.

Return type:

bytes | None

Returns:

None (SimpleACK response).

Raises:

BACnetError – If the target object does not exist.

Parameters:
async handle_confirmed_event_notification(service_choice, data, source)[source]

Handle ConfirmedEventNotification-Request per Clause 13.8.

Logs the notification at debug level.

Return type:

bytes | None

Returns:

None (SimpleACK response).

Parameters:
async handle_unconfirmed_event_notification(service_choice, data, source)[source]

Handle UnconfirmedEventNotification-Request per Clause 13.9.

Logs the notification at debug level.

Return type:

None

Parameters:
async handle_get_alarm_summary(service_choice, data, source)[source]

Handle GetAlarmSummary-Request per Clause 13.6.

Scans all objects in the database for those in an alarm (non-NORMAL) event state and returns their summaries.

Return type:

bytes

Returns:

Encoded GetAlarmSummary-ACK service data.

Parameters:
async handle_get_enrollment_summary(service_choice, data, source)[source]

Handle GetEnrollmentSummary-Request per Clause 13.7.

Iterates EventEnrollment objects, applies the request filters, and returns matching enrollment summaries.

Return type:

bytes

Returns:

Encoded GetEnrollmentSummary-ACK service data.

Parameters:
async handle_get_event_information(service_choice, data, source)[source]

Handle GetEventInformation-Request per Clause 13.12.

Iterates all objects in the database for those in an alarm (non-NORMAL) event state and returns event summaries with pagination support.

Return type:

bytes

Returns:

Encoded GetEventInformation-ACK service data.

Parameters:
async handle_who_has(service_choice, data, source)[source]

Handle Who-Has-Request per Clause 16.9.

Searches the local object database for a matching object and responds with I-Have if found.

Return type:

None

Parameters:
async handle_confirmed_text_message(service_choice, data, source)[source]

Handle ConfirmedTextMessage-Request per Clause 16.5.

Logs the message and invokes the text message callback if set.

Return type:

bytes | None

Returns:

None (SimpleACK response).

Parameters:
async handle_unconfirmed_text_message(service_choice, data, source)[source]

Handle UnconfirmedTextMessage-Request per Clause 16.6.

Logs the message and invokes the text message callback if set.

Return type:

None

Parameters:
async handle_write_group(service_choice, data, source)[source]

Handle WriteGroup-Request per Clause 15.11.

Looks up Channel objects by group number and writes values.

Return type:

None

Parameters:
async handle_who_am_i(service_choice, data, source)[source]

Handle Who-Am-I-Request per Clause 16.11.

Logs the request. A supervisor application should register a callback to handle device identity assignment.

Return type:

None

Parameters:
async handle_you_are(service_choice, data, source)[source]

Handle You-Are-Request per Clause 16.11.

Applies the assigned device identity if the device is unconfigured.

Return type:

None

Parameters:
async handle_vt_open(service_choice, data, source)[source]

Handle VT-Open-Request per Clause 17.1.

Return type:

bytes

Returns:

Encoded VT-Open-ACK with remote session ID.

Raises:

BACnetError – If no VT sessions are available or the VT class is not supported.

Parameters:
async handle_vt_close(service_choice, data, source)[source]

Handle VT-Close-Request per Clause 17.2.

Return type:

bytes | None

Returns:

None (SimpleACK response).

Raises:

BACnetError – If a session ID is unknown.

Parameters:
async handle_vt_data(service_choice, data, source)[source]

Handle VT-Data-Request per Clause 17.3.

Return type:

bytes

Returns:

Encoded VT-Data-ACK.

Raises:

BACnetError – If the session is unknown.

Parameters:
async handle_audit_log_query(service_choice, data, source)[source]

Handle AuditLogQuery-Request per Clause 13.19.

Queries the specified Audit Log object’s buffer.

Return type:

bytes

Returns:

Encoded AuditLogQuery-ACK.

Raises:

BACnetError – If the audit log object is not found.

Parameters:
async handle_confirmed_audit_notification(service_choice, data, source)[source]

Handle ConfirmedAuditNotification-Request per Clause 13.20.

Appends notifications to Audit Log objects.

Return type:

bytes | None

Returns:

None (SimpleACK response).

Parameters:
async handle_unconfirmed_audit_notification(service_choice, data, source)[source]

Handle UnconfirmedAuditNotification-Request per Clause 13.21.

Appends notifications to Audit Log objects.

Return type:

None

Parameters:

Transaction State Machine

Transaction State Machines per ASHRAE 135-2016 Clause 5.4.

class bac_py.app.tsm.ClientTransactionState(*values)[source]

Bases: IntEnum

Client TSM states per Clause 5.4.4.

IDLE = 0
SEGMENTED_REQUEST = 1
AWAIT_CONFIRMATION = 2
SEGMENTED_CONFIRMATION = 3
class bac_py.app.tsm.ClientTransaction(invoke_id, destination, service_choice, request_data, future, retry_count=0, timeout_handle=None, state=ClientTransactionState.IDLE, segment_sender=None, segment_receiver=None, seg_retry_count=0)[source]

Bases: object

Tracks an outstanding confirmed service request.

Parameters:
invoke_id: int
destination: BACnetAddress
service_choice: int
request_data: bytes
future: Future[bytes]
retry_count: int = 0
timeout_handle: TimerHandle | None = None
state: ClientTransactionState = 0
segment_sender: SegmentSender | None = None
segment_receiver: SegmentReceiver | None = None
seg_retry_count: int = 0
class bac_py.app.tsm.ClientTSM(network, *, apdu_timeout=6.0, apdu_retries=3, max_apdu_length=1476, max_segments=None, segment_timeout=2.0, proposed_window_size=16)[source]

Bases: object

Client Transaction State Machine (Clause 5.4.4).

Manages outstanding confirmed requests, correlating responses by (source_address, invoke_id).

Parameters:
  • network (NetworkSender)

  • apdu_timeout (float)

  • apdu_retries (int)

  • max_apdu_length (int)

  • max_segments (int | None)

  • segment_timeout (float)

  • proposed_window_size (int)

async send_request(service_choice, request_data, destination, *, max_apdu_override=None)[source]

Send a confirmed request and await the response.

If the request data exceeds the max segment payload, the request is automatically segmented per Clause 5.2.

Parameters:
  • service_choice (int) – Confirmed service choice number.

  • request_data (bytes) – Encoded service request bytes.

  • destination (BACnetAddress) – Target device address.

  • max_apdu_override (int | None (default: None)) – When provided, constrains the effective max APDU length for segmentation decisions (Clause 19.4). Typically set to min(local, remote) based on cached peer device info from I-Am responses.

Return type:

bytes

Returns:

The service-ack data from ComplexACK, or empty bytes for SimpleACK.

Raises:
handle_simple_ack(source, invoke_id, service_choice)[source]

Handle a SimpleACK response.

Return type:

None

Parameters:
handle_complex_ack(source, invoke_id, service_choice, data)[source]

Handle a ComplexACK response (non-segmented).

Return type:

None

Parameters:
handle_error(source, invoke_id, error_class, error_code, error_data=b'')[source]

Handle an Error-PDU response.

Return type:

None

Parameters:
handle_reject(source, invoke_id, reason)[source]

Handle a Reject-PDU response.

Return type:

None

Parameters:
handle_abort(source, invoke_id, reason)[source]

Handle an Abort-PDU response.

Return type:

None

Parameters:
handle_segment_ack(source, pdu)[source]

Handle SegmentACK during segmented request sending.

Return type:

None

Parameters:
handle_segmented_complex_ack(source, pdu)[source]

Handle a segmented ComplexACK response.

Return type:

None

Parameters:
active_transactions()[source]

Return all active transactions (for shutdown).

Return type:

list[ClientTransaction]

class bac_py.app.tsm.ServerTransactionState(*values)[source]

Bases: IntEnum

Server TSM states per Clause 5.4.5.

IDLE = 0
SEGMENTED_REQUEST = 1
AWAIT_RESPONSE = 2
SEGMENTED_RESPONSE = 3
class bac_py.app.tsm.ServerTransaction(invoke_id, source, service_choice, state=ServerTransactionState.IDLE, cached_response=None, timeout_handle=None, segment_receiver=None, segment_sender=None, seg_retry_count=0, client_max_apdu_length=1476, client_max_segments=None, segmented_response_accepted=False)[source]

Bases: object

Tracks an incoming confirmed request being processed.

Parameters:
invoke_id: int
source: BACnetAddress
service_choice: int
state: ServerTransactionState = 0
cached_response: bytes | None = None
timeout_handle: TimerHandle | None = None
segment_receiver: SegmentReceiver | None = None
segment_sender: SegmentSender | None = None
seg_retry_count: int = 0
client_max_apdu_length: int = 1476
client_max_segments: int | None = None
segmented_response_accepted: bool = False
class bac_py.app.tsm.ServerTSM(network, *, request_timeout=6.0, apdu_retries=3, segment_timeout=2.0, max_apdu_length=1476, max_segments=None, proposed_window_size=16)[source]

Bases: object

Server Transaction State Machine (Clause 5.4.5).

Prevents duplicate processing and caches responses for retransmission detection.

Parameters:
  • network (NetworkSender)

  • request_timeout (float)

  • apdu_retries (int)

  • segment_timeout (float)

  • max_apdu_length (int)

  • max_segments (int | None)

  • proposed_window_size (int)

receive_confirmed_request(pdu, source)[source]

Register an incoming confirmed request.

Returns (txn, service_data) for new requests. service_data is the complete request payload for non-segmented requests, or None for segmented requests that need more segments.

Returns None for duplicates (cached response is resent).

Return type:

tuple[ServerTransaction, bytes | None] | None

Parameters:
handle_request_segment(pdu, source)[source]

Handle a subsequent segment of a segmented confirmed request.

Returns (txn, complete_data) when all segments are received, (txn, None) when more segments expected, or None if no matching transaction.

Return type:

tuple[ServerTransaction, bytes | None] | None

Parameters:
start_segmented_response(txn, service_choice, response_data)[source]

Begin sending a segmented ComplexACK response.

Parameters:
  • txn (ServerTransaction) – The server transaction.

  • service_choice (int) – Service choice for the response.

  • response_data (bytes) – The complete service-ack data to segment.

Return type:

None

handle_segment_ack_for_response(source, pdu)[source]

Handle SegmentACK from client during segmented response sending.

Return type:

None

Parameters:
complete_transaction(txn, response_apdu)[source]

Cache the response and schedule cleanup.

Return type:

None

Parameters: