Device Management

Service Base

Service handler registration and dispatch per ASHRAE 135-2016.

type bac_py.services.base.ConfirmedHandler = Callable[[int, bytes, BACnetAddress], Awaitable[bytes | None]]
type bac_py.services.base.UnconfirmedHandler = Callable[[int, bytes, BACnetAddress], Awaitable[None]]
class bac_py.services.base.ServiceRegistry[source]

Bases: object

Registry for BACnet service request handlers.

Maps service choice numbers to handler coroutines for both confirmed and unconfirmed services.

register_confirmed(service_choice, handler)[source]

Register a handler for a confirmed service.

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

  • handler (TypeAliasType) – Async handler coroutine.

Return type:

None

register_unconfirmed(service_choice, handler)[source]

Register a handler for an unconfirmed service.

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

  • handler (TypeAliasType) – Async handler coroutine.

Return type:

None

async dispatch_confirmed(service_choice, request_data, source)[source]

Dispatch an incoming confirmed request to its handler.

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

  • request_data (bytes) – Raw service request bytes.

  • source (BACnetAddress) – Source address of the request.

Return type:

bytes | None

Returns:

Service ACK data for ComplexACK, or None for SimpleACK.

Raises:

BACnetRejectError – If no handler is registered for the service.

async dispatch_unconfirmed(service_choice, request_data, source)[source]

Dispatch an incoming unconfirmed request to its handler.

If no handler is registered for service_choice, the request is silently ignored (per Clause 5.4.2 – no reject/abort is sent for unconfirmed services).

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

  • request_data (bytes) – Raw service request bytes.

  • source (BACnetAddress) – Source address of the request.

Return type:

None

has_confirmed_handler(service_choice)[source]

Check whether a confirmed service handler is registered.

Parameters:

service_choice (int) – Confirmed service choice number.

Return type:

bool

Returns:

True if a handler is registered.

has_unconfirmed_handler(service_choice)[source]

Check whether an unconfirmed service handler is registered.

Parameters:

service_choice (int) – Unconfirmed service choice number.

Return type:

bool

Returns:

True if a handler is registered.

Common Types

Shared BACnet service data types per ASHRAE 135-2016 Clause 21.

class bac_py.services.common.BACnetPropertyValue(property_identifier, property_array_index=None, value=b'', priority=None)[source]

Bases: object

BACnetPropertyValue per Clause 21.

BACnetPropertyValue ::= SEQUENCE {
    propertyIdentifier  [0] BACnetPropertyIdentifier,
    propertyArrayIndex  [1] Unsigned OPTIONAL,
    value               [2] ABSTRACT-SYNTAX.&Type,
    priority            [3] Unsigned (1..16) OPTIONAL
}

The value field contains raw application-tagged bytes.

Parameters:
property_identifier: PropertyIdentifier
property_array_index: int | None
value: bytes
priority: int | None
encode()[source]

Encode BACnetPropertyValue.

Return type:

bytes

Returns:

Encoded bytes for this property value sequence.

classmethod decode_from(data, offset=0)[source]

Decode BACnetPropertyValue from data at a given offset.

Parameters:
  • data (memoryview | bytes) – Raw bytes containing encoded property value data.

  • offset (int (default: 0)) – Byte offset to start decoding from.

Return type:

tuple[BACnetPropertyValue, int]

Returns:

Tuple of (decoded BACnetPropertyValue, new offset).

Raises:

BACnetRejectError – If priority is outside the 1–16 range.

Errors

BACnet protocol error types per ASHRAE 135-2016 Clause 18.

exception bac_py.services.errors.BACnetBaseError[source]

Bases: Exception

Base exception for BACnet protocol errors.

exception bac_py.services.errors.BACnetError(error_class, error_code, error_data=b'')[source]

Bases: BACnetBaseError

BACnet Error-PDU received (Clause 18).

Contains error class and code per the specification.

Parameters:
Return type:

None

exception bac_py.services.errors.BACnetRejectError(reason)[source]

Bases: BACnetBaseError

BACnet Reject-PDU received (Clause 18.9).

Indicates a syntax or protocol error in the request.

Parameters:

reason (RejectReason)

Return type:

None

exception bac_py.services.errors.BACnetAbortError(reason)[source]

Bases: BACnetBaseError

BACnet Abort-PDU received (Clause 18.10).

Indicates the transaction was aborted.

Parameters:

reason (AbortReason)

Return type:

None

exception bac_py.services.errors.BACnetTimeoutError[source]

Bases: BACnetBaseError

Request timed out after all retries exhausted.

Device Management

Device management services per ASHRAE 135-2016 Clause 16.

DeviceCommunicationControl (Clause 16.1), ReinitializeDevice (Clause 16.4), TimeSynchronization (Clause 16.7), and UTCTimeSynchronization (Clause 16.8).

class bac_py.services.device_mgmt.DeviceCommunicationControlRequest(enable_disable, time_duration=None, password=None)[source]

Bases: object

DeviceCommunicationControl-Request (Clause 16.1.1).

DeviceCommunicationControl-Request ::= SEQUENCE {
    timeDuration  [0] Unsigned16 OPTIONAL,
    enable-disable [1] ENUMERATED,
    password      [2] CharacterString (1..20) OPTIONAL
}
Parameters:
enable_disable: EnableDisable
time_duration: int | None
password: str | None
encode()[source]

Encode DeviceCommunicationControl-Request service parameters.

Return type:

bytes

Returns:

Encoded service request bytes.

classmethod decode(data)[source]

Decode DeviceCommunicationControl-Request from service request bytes.

Parameters:

data (memoryview | bytes) – Raw service request bytes.

Return type:

DeviceCommunicationControlRequest

Returns:

Decoded DeviceCommunicationControlRequest.

Raises:

BACnetRejectError – If a parameter is out of range.

class bac_py.services.device_mgmt.ReinitializeDeviceRequest(reinitialized_state, password=None)[source]

Bases: object

ReinitializeDevice-Request (Clause 16.4.1).

ReinitializeDevice-Request ::= SEQUENCE {
    reinitializedStateOfDevice  [0] ENUMERATED,
    password                    [1] CharacterString (1..20) OPTIONAL
}
Parameters:
reinitialized_state: ReinitializedState
password: str | None
encode()[source]

Encode ReinitializeDevice-Request service parameters.

Return type:

bytes

Returns:

Encoded service request bytes.

classmethod decode(data)[source]

Decode ReinitializeDevice-Request from service request bytes.

Parameters:

data (memoryview | bytes) – Raw service request bytes.

Return type:

ReinitializeDeviceRequest

Returns:

Decoded ReinitializeDeviceRequest.

Raises:

BACnetRejectError – If the password is out of range.

class bac_py.services.device_mgmt.TimeSynchronizationRequest(date, time)[source]

Bases: object

TimeSynchronization-Request (Clause 16.7.1).

TimeSynchronization-Request ::= SEQUENCE {
    date  Date,
    time  Time
}

Both fields are APPLICATION-tagged (not context).

Parameters:
date: BACnetDate
time: BACnetTime
encode()[source]

Encode TimeSynchronization-Request service parameters.

Return type:

bytes

Returns:

Encoded service request bytes.

classmethod decode(data)[source]

Decode TimeSynchronization-Request from service request bytes.

Parameters:

data (memoryview | bytes) – Raw service request bytes.

Return type:

Self

Returns:

Decoded request instance.

class bac_py.services.device_mgmt.UTCTimeSynchronizationRequest(date, time)[source]

Bases: TimeSynchronizationRequest

UTCTimeSynchronization-Request (Clause 16.8.1).

Same structure as TimeSynchronizationRequest but uses UnconfirmedServiceChoice.UTC_TIME_SYNCHRONIZATION (9).

Parameters:

Object Management

Object management services per ASHRAE 135-2016 Clause 15.3-15.4.

CreateObject (Clause 15.3), DeleteObject (Clause 15.4).

class bac_py.services.object_mgmt.CreateObjectRequest(object_type=None, object_identifier=None, list_of_initial_values=None)[source]

Bases: object

CreateObject-Request (Clause 15.3.1.1).

CreateObject-Request ::= SEQUENCE {
    objectSpecifier  [0] CHOICE {
        objectType        [0] BACnetObjectType,
        objectIdentifier  [1] BACnetObjectIdentifier
    },
    listOfInitialValues  [1] SEQUENCE OF BACnetPropertyValue OPTIONAL
}
Parameters:
object_type: ObjectType | None
object_identifier: ObjectIdentifier | None
list_of_initial_values: list[BACnetPropertyValue] | None
encode()[source]

Encode CreateObject-Request service parameters.

Return type:

bytes

Returns:

Encoded service request bytes.

classmethod decode(data)[source]

Decode CreateObject-Request from service request bytes.

Parameters:

data (memoryview | bytes) – Raw service request bytes.

Return type:

CreateObjectRequest

Returns:

Decoded CreateObjectRequest.

Raises:

ValueError – If the objectSpecifier CHOICE tag is unrecognized.

class bac_py.services.object_mgmt.DeleteObjectRequest(object_identifier)[source]

Bases: object

DeleteObject-Request (Clause 15.4.1.1).

DeleteObject-Request ::= SEQUENCE {
    objectIdentifier  BACnetObjectIdentifier
}

The objectIdentifier is APPLICATION-tagged.

Parameters:

object_identifier (ObjectIdentifier)

object_identifier: ObjectIdentifier
encode()[source]

Encode DeleteObject-Request service parameters.

Return type:

bytes

Returns:

Encoded service request bytes.

classmethod decode(data)[source]

Decode DeleteObject-Request from service request bytes.

Parameters:

data (memoryview | bytes) – Raw service request bytes.

Return type:

DeleteObjectRequest

Returns:

Decoded DeleteObjectRequest.

List Element

List element services per ASHRAE 135-2016 Clause 15.1-15.2.

AddListElement (Clause 15.1), RemoveListElement (Clause 15.2).

class bac_py.services.list_element.AddListElementRequest(object_identifier, property_identifier, list_of_elements, property_array_index=None)[source]

Bases: _ListElementRequest

AddListElement-Request (Clause 15.1.1.1).

Parameters:
class bac_py.services.list_element.RemoveListElementRequest(object_identifier, property_identifier, list_of_elements, property_array_index=None)[source]

Bases: _ListElementRequest

RemoveListElement-Request (Clause 15.2.1.1).

Parameters:

File Access

Atomic file access services per ASHRAE 135-2016 Clause 14.

AtomicReadFile (Clause 14.1), AtomicWriteFile (Clause 14.2).

class bac_py.services.file_access.StreamReadAccess(file_start_position, requested_octet_count)[source]

Bases: object

Stream access parameters for AtomicReadFile-Request.

Parameters:
  • file_start_position (int)

  • requested_octet_count (int)

file_start_position: int
requested_octet_count: int
class bac_py.services.file_access.RecordReadAccess(file_start_record, requested_record_count)[source]

Bases: object

Record access parameters for AtomicReadFile-Request.

Parameters:
  • file_start_record (int)

  • requested_record_count (int)

file_start_record: int
requested_record_count: int
class bac_py.services.file_access.AtomicReadFileRequest(file_identifier, access_method)[source]

Bases: object

AtomicReadFile-Request (Clause 14.1.1.1).

AtomicReadFile-Request ::= SEQUENCE {
    fileIdentifier    BACnetObjectIdentifier,
    accessMethod      CHOICE {
        streamAccess  [0] SEQUENCE {
            fileStartPosition      INTEGER,
            requestedOctetCount    Unsigned
        },
        recordAccess  [1] SEQUENCE {
            fileStartRecord        INTEGER,
            requestedRecordCount   Unsigned
        }
    }
}
Parameters:
file_identifier: ObjectIdentifier
access_method: StreamReadAccess | RecordReadAccess
encode()[source]

Encode AtomicReadFile-Request service parameters.

Return type:

bytes

Returns:

Encoded service request bytes.

classmethod decode(data)[source]

Decode AtomicReadFile-Request from service request bytes.

Parameters:

data (memoryview | bytes) – Raw service request bytes.

Return type:

AtomicReadFileRequest

Returns:

Decoded AtomicReadFileRequest.

Raises:

ValueError – If the access method CHOICE tag is unrecognized.

class bac_py.services.file_access.StreamReadACK(file_start_position, file_data)[source]

Bases: object

Stream access result for AtomicReadFile-ACK.

Parameters:
  • file_start_position (int)

  • file_data (bytes)

file_start_position: int
file_data: bytes
class bac_py.services.file_access.RecordReadACK(file_start_record, returned_record_count, file_record_data)[source]

Bases: object

Record access result for AtomicReadFile-ACK.

Parameters:
  • file_start_record (int)

  • returned_record_count (int)

  • file_record_data (list[bytes])

file_start_record: int
returned_record_count: int
file_record_data: list[bytes]
class bac_py.services.file_access.AtomicReadFileACK(end_of_file, access_method)[source]

Bases: object

AtomicReadFile-ACK (Clause 14.1.1.2).

AtomicReadFile-ACK ::= SEQUENCE {
    endOfFile       BOOLEAN,
    accessMethod    CHOICE {
        streamAccess  [0] SEQUENCE {
            fileStartPosition  INTEGER,
            fileData           OCTET STRING
        },
        recordAccess  [1] SEQUENCE {
            fileStartRecord      INTEGER,
            returnedRecordCount  Unsigned,
            fileRecordData       SEQUENCE OF OCTET STRING
        }
    }
}
Parameters:
end_of_file: bool
access_method: StreamReadACK | RecordReadACK
encode()[source]

Encode AtomicReadFile-ACK service parameters.

Return type:

bytes

Returns:

Encoded service ACK bytes.

classmethod decode(data)[source]

Decode AtomicReadFile-ACK from service ACK bytes.

Parameters:

data (memoryview | bytes) – Raw service ACK bytes.

Return type:

AtomicReadFileACK

Returns:

Decoded AtomicReadFileACK.

Raises:

ValueError – If the access method CHOICE tag is unrecognized.

class bac_py.services.file_access.StreamWriteAccess(file_start_position, file_data)[source]

Bases: object

Stream access parameters for AtomicWriteFile-Request.

Parameters:
  • file_start_position (int)

  • file_data (bytes)

file_start_position: int
file_data: bytes
class bac_py.services.file_access.RecordWriteAccess(file_start_record, record_count, file_record_data)[source]

Bases: object

Record access parameters for AtomicWriteFile-Request.

Parameters:
  • file_start_record (int)

  • record_count (int)

  • file_record_data (list[bytes])

file_start_record: int
record_count: int
file_record_data: list[bytes]
class bac_py.services.file_access.AtomicWriteFileRequest(file_identifier, access_method)[source]

Bases: object

AtomicWriteFile-Request (Clause 14.2.1.1).

AtomicWriteFile-Request ::= SEQUENCE {
    fileIdentifier    BACnetObjectIdentifier,
    accessMethod      CHOICE {
        streamAccess  [0] SEQUENCE {
            fileStartPosition  INTEGER,
            fileData           OCTET STRING
        },
        recordAccess  [1] SEQUENCE {
            fileStartRecord    INTEGER,
            recordCount        Unsigned,
            fileRecordData     SEQUENCE OF OCTET STRING
        }
    }
}
Parameters:
file_identifier: ObjectIdentifier
access_method: StreamWriteAccess | RecordWriteAccess
encode()[source]

Encode AtomicWriteFile-Request service parameters.

Return type:

bytes

Returns:

Encoded service request bytes.

classmethod decode(data)[source]

Decode AtomicWriteFile-Request from service request bytes.

Parameters:

data (memoryview | bytes) – Raw service request bytes.

Return type:

AtomicWriteFileRequest

Returns:

Decoded AtomicWriteFileRequest.

Raises:

ValueError – If the access method CHOICE tag is unrecognized.

class bac_py.services.file_access.AtomicWriteFileACK(is_stream, file_start)[source]

Bases: object

AtomicWriteFile-ACK (Clause 14.2.1.2).

AtomicWriteFile-ACK ::= CHOICE {
    fileStartPosition  [0] INTEGER,
    fileStartRecord    [1] INTEGER
}
Parameters:
  • is_stream (bool)

  • file_start (int)

is_stream: bool
file_start: int
encode()[source]

Encode AtomicWriteFile-ACK service parameters.

Return type:

bytes

Returns:

Encoded service ACK bytes.

classmethod decode(data)[source]

Decode AtomicWriteFile-ACK from service ACK bytes.

Parameters:

data (memoryview | bytes) – Raw service ACK bytes.

Return type:

AtomicWriteFileACK

Returns:

Decoded AtomicWriteFileACK.

Private Transfer

Private transfer services per ASHRAE 135-2016 Clause 16.2-16.3.

ConfirmedPrivateTransfer (Clause 16.2), UnconfirmedPrivateTransfer (Clause 16.3).

class bac_py.services.private_transfer.ConfirmedPrivateTransferRequest(vendor_id, service_number, service_parameters=None)[source]

Bases: object

ConfirmedPrivateTransfer-Request (Clause 16.2.1.1).

ConfirmedPrivateTransfer-Request ::= SEQUENCE {
    vendorID          [0] Unsigned,
    serviceNumber     [1] Unsigned,
    serviceParameters [2] ABSTRACT-SYNTAX.&TYPE OPTIONAL
}
Parameters:
  • vendor_id (int)

  • service_number (int)

  • service_parameters (bytes | None)

vendor_id: int
service_number: int
service_parameters: bytes | None
encode()[source]

Encode ConfirmedPrivateTransfer-Request service parameters.

Return type:

bytes

Returns:

Encoded service request bytes.

classmethod decode(data)[source]

Decode ConfirmedPrivateTransfer-Request from service request bytes.

Parameters:

data (memoryview | bytes) – Raw service request bytes.

Return type:

Self

Returns:

Decoded request instance.

class bac_py.services.private_transfer.ConfirmedPrivateTransferACK(vendor_id, service_number, result_block=None)[source]

Bases: object

ConfirmedPrivateTransfer-ACK (Clause 16.2.1.2).

Uses the same wire format as the request, but the context-2 tagged block carries vendor-defined result data rather than request service-parameters.

Parameters:
  • vendor_id (int)

  • service_number (int)

  • result_block (bytes | None)

vendor_id: int
service_number: int
result_block: bytes | None
encode()[source]

Encode ConfirmedPrivateTransfer-ACK service parameters.

Return type:

bytes

Returns:

Encoded service ACK bytes.

classmethod decode(data)[source]

Decode ConfirmedPrivateTransfer-ACK from service ACK bytes.

Parameters:

data (memoryview | bytes) – Raw service ACK bytes.

Return type:

ConfirmedPrivateTransferACK

Returns:

Decoded ConfirmedPrivateTransferACK.

class bac_py.services.private_transfer.UnconfirmedPrivateTransferRequest(vendor_id, service_number, service_parameters=None)[source]

Bases: ConfirmedPrivateTransferRequest

UnconfirmedPrivateTransfer-Request (Clause 16.3.1.1).

Same structure as ConfirmedPrivateTransfer-Request.

Parameters:
  • vendor_id (int)

  • service_number (int)

  • service_parameters (bytes | None)

Virtual Terminal

Virtual terminal services per ASHRAE 135-2020 Clause 17.

VT-Open (Clause 17.1), VT-Close (Clause 17.2), VT-Data (Clause 17.3).

class bac_py.services.virtual_terminal.VTOpenRequest(vt_class, local_vt_session_identifier)[source]

Bases: object

VT-Open-Request (Clause 17.1.1).

VT-Open-Request ::= SEQUENCE {
    vtClass                    BACnetVTClass,
    localVTSessionIdentifier   Unsigned8
}
Parameters:
  • vt_class (VTClass)

  • local_vt_session_identifier (int)

vt_class: VTClass
local_vt_session_identifier: int
encode()[source]

Encode VT-Open-Request service parameters.

Return type:

bytes

classmethod decode(data)[source]

Decode VT-Open-Request from service request bytes.

Return type:

VTOpenRequest

Parameters:

data (memoryview | bytes)

class bac_py.services.virtual_terminal.VTOpenACK(remote_vt_session_identifier)[source]

Bases: object

VT-Open-ACK (Clause 17.1.2).

VT-Open-ACK ::= SEQUENCE {
    remoteVTSessionIdentifier  Unsigned8
}
Parameters:

remote_vt_session_identifier (int)

remote_vt_session_identifier: int
encode()[source]

Encode VT-Open-ACK service parameters.

Return type:

bytes

classmethod decode(data)[source]

Decode VT-Open-ACK from service request bytes.

Return type:

VTOpenACK

Parameters:

data (memoryview | bytes)

class bac_py.services.virtual_terminal.VTCloseRequest(list_of_remote_vt_session_identifiers)[source]

Bases: object

VT-Close-Request (Clause 17.2.1).

VT-Close-Request ::= SEQUENCE {
    listOfRemoteVTSessionIdentifiers  SEQUENCE OF Unsigned8
}
Parameters:

list_of_remote_vt_session_identifiers (list[int])

list_of_remote_vt_session_identifiers: list[int]
encode()[source]

Encode VT-Close-Request service parameters.

Return type:

bytes

classmethod decode(data)[source]

Decode VT-Close-Request from service request bytes.

Return type:

VTCloseRequest

Parameters:

data (memoryview | bytes)

class bac_py.services.virtual_terminal.VTDataRequest(vt_session_identifier, vt_new_data, vt_data_flag)[source]

Bases: object

VT-Data-Request (Clause 17.3.1).

VT-Data-Request ::= SEQUENCE {
    vtSessionIdentifier  Unsigned8,
    vtNewData            OCTET STRING,
    vtDataFlag           BOOLEAN
}
Parameters:
  • vt_session_identifier (int)

  • vt_new_data (bytes)

  • vt_data_flag (bool)

vt_session_identifier: int
vt_new_data: bytes
vt_data_flag: bool
encode()[source]

Encode VT-Data-Request service parameters.

Return type:

bytes

classmethod decode(data)[source]

Decode VT-Data-Request from service request bytes.

Return type:

VTDataRequest

Parameters:

data (memoryview | bytes)

class bac_py.services.virtual_terminal.VTDataACK(all_new_data_accepted, accepted_octet_count=None)[source]

Bases: object

VT-Data-ACK (Clause 17.3.2).

VT-Data-ACK ::= SEQUENCE {
    allNewDataAccepted  BOOLEAN,
    acceptedOctetCount  Unsigned OPTIONAL
}

accepted_octet_count is present only when all_new_data_accepted is False.

Parameters:
  • all_new_data_accepted (bool)

  • accepted_octet_count (int | None)

all_new_data_accepted: bool
accepted_octet_count: int | None
encode()[source]

Encode VT-Data-ACK service parameters.

Return type:

bytes

classmethod decode(data)[source]

Decode VT-Data-ACK from service request bytes.

Return type:

VTDataACK

Parameters:

data (memoryview | bytes)

Write Group

WriteGroup service per ASHRAE 135-2020 Clause 15.11.

WriteGroup is an unconfirmed service for writing channel values via group addressing.

class bac_py.services.write_group.GroupChannelValue(channel, value, overriding_priority=None)[source]

Bases: object

A single channel value in a WriteGroup change list.

GroupChannelValue ::= SEQUENCE {
    channel             [0] Unsigned16,
    overridingPriority  [1] Unsigned (1..16) OPTIONAL,
    value               ABSTRACT-SYNTAX.&TYPE
}
Parameters:
  • channel (int)

  • value (bytes)

  • overriding_priority (int | None)

channel: int
value: bytes
overriding_priority: int | None
encode()[source]

Encode a single GroupChannelValue.

Return type:

bytes

classmethod decode(data, offset)[source]

Decode a single GroupChannelValue starting at offset.

Return type:

tuple[GroupChannelValue, int]

Returns:

Tuple of (decoded value, new offset).

Parameters:
class bac_py.services.write_group.WriteGroupRequest(group_number, write_priority, change_list)[source]

Bases: object

WriteGroup-Request (Clause 15.11.1).

WriteGroup-Request ::= SEQUENCE {
    groupNumber    [0] Unsigned32,
    writePriority  [1] Unsigned (1..16),
    changeList     [2] SEQUENCE OF GroupChannelValue
}
Parameters:
group_number: int
write_priority: int
change_list: list[GroupChannelValue]
encode()[source]

Encode WriteGroup-Request service parameters.

Return type:

bytes

classmethod decode(data)[source]

Decode WriteGroup-Request from service request bytes.

Return type:

Self

Parameters:

data (memoryview | bytes)