Encoding¶
ASN.1 Basic Encoding Rules (BER) implementation for BACnet tag-length-value encoding, primitive type serialization, and APDU framing. Most users interact with encoding indirectly through the service and type layers.
ASN.1 tag-length-value encoding and decoding.
Primitive Encoding¶
BACnet primitive type encoding/decoding per ASHRAE 135-2016 Clause 20.2.
- bac_py.encoding.primitives.encode_unsigned(value)[source]¶
Encode an unsigned integer using the minimum number of octets, big-endian.
BACnet unsigned integers are at most 4 bytes (0..4,294,967,295).
- Parameters:
value (
int) – Non-negative integer to encode (0–4,294,967,295).- Return type:
- Returns:
Big-endian encoded bytes (1–4 bytes).
- Raises:
ValueError – If value is negative or exceeds the 4-byte maximum.
- bac_py.encoding.primitives.decode_unsigned(data)[source]¶
Decode an unsigned integer from big-endian bytes.
- Parameters:
data (
memoryview|bytes) – One or more bytes encoding a big-endian unsigned integer.- Return type:
- Returns:
The decoded non-negative integer.
- bac_py.encoding.primitives.encode_unsigned64(value)[source]¶
Encode an unsigned integer using the minimum number of octets, up to 8 bytes.
Used for BACnet Unsigned64 fields such as audit log sequence numbers.
- Parameters:
value (
int) – Non-negative integer to encode (0–18,446,744,073,709,551,615).- Return type:
- Returns:
Big-endian encoded bytes (1–8 bytes).
- Raises:
ValueError – If value is negative or exceeds the 8-byte maximum.
- bac_py.encoding.primitives.decode_unsigned64(data)[source]¶
Decode an unsigned integer from big-endian bytes (up to 8 bytes).
- Parameters:
data (
memoryview|bytes) – One or more bytes encoding a big-endian unsigned integer.- Return type:
- Returns:
The decoded non-negative integer.
- bac_py.encoding.primitives.encode_signed(value)[source]¶
Encode a signed integer using minimum octets, two’s complement, big-endian.
BACnet signed integers are at most 4 bytes (-2,147,483,648..2,147,483,647).
- Parameters:
value (
int) – Signed integer to encode (-2,147,483,648..2,147,483,647).- Return type:
- Returns:
Two’s-complement big-endian encoded bytes (1–4 bytes).
- Raises:
ValueError – If value is outside the 4-byte signed range.
- bac_py.encoding.primitives.decode_signed(data)[source]¶
Decode a signed integer from two’s-complement big-endian bytes.
- Parameters:
data (
memoryview|bytes) – One or more bytes encoding a two’s-complement big-endian signed integer.- Return type:
- Returns:
The decoded signed integer.
- bac_py.encoding.primitives.encode_real(value)[source]¶
Encode an IEEE-754 single-precision (32-bit) float.
- bac_py.encoding.primitives.decode_real(data)[source]¶
Decode an IEEE-754 single-precision (32-bit) float.
- Parameters:
data (
memoryview|bytes) – At least 4 bytes of big-endian IEEE-754 single-precision data.- Return type:
- Returns:
The decoded floating-point value.
- Raises:
ValueError – If data contains fewer than 4 bytes.
- bac_py.encoding.primitives.encode_double(value)[source]¶
Encode an IEEE-754 double-precision (64-bit) float.
- bac_py.encoding.primitives.decode_double(data)[source]¶
Decode an IEEE-754 double-precision (64-bit) float.
- Parameters:
data (
memoryview|bytes) – At least 8 bytes of big-endian IEEE-754 double-precision data.- Return type:
- Returns:
The decoded floating-point value.
- Raises:
ValueError – If data contains fewer than 8 bytes.
- bac_py.encoding.primitives.decode_octet_string(data)[source]¶
Decode an octet string by copying the raw bytes.
- Parameters:
data (
memoryview|bytes) – Raw octet-string content bytes.- Return type:
- Returns:
A copy of the input as a
bytesobject.
- bac_py.encoding.primitives.encode_character_string(value, charset=0)[source]¶
Encode a character string with a leading charset byte.
- Parameters:
- Return type:
- Returns:
Encoded bytes with leading charset byte.
- Raises:
ValueError – If charset is not a supported BACnet character set.
- bac_py.encoding.primitives.decode_character_string(data)[source]¶
Decode a character string from contents octets.
The first byte is the character set identifier. Unknown charsets fall back to latin-1 decoding with a warning log rather than raising, to preserve data from devices using non-standard charsets.
- Parameters:
data (
memoryview|bytes) – Contents octets with leading charset byte.- Return type:
- Returns:
The decoded Python string.
- Raises:
ValueError – If data is empty.
- bac_py.encoding.primitives.encode_enumerated(value)[source]¶
Encode an enumerated value (same encoding as unsigned).
- bac_py.encoding.primitives.decode_enumerated(data)[source]¶
Decode an enumerated value (same encoding as unsigned).
- Parameters:
data (
memoryview|bytes) – Big-endian encoded bytes.- Return type:
- Returns:
The decoded enumerated value as an integer.
- bac_py.encoding.primitives.encode_bit_string(value)[source]¶
Encode a bit string with a leading unused-bits count byte.
- bac_py.encoding.primitives.decode_bit_string(data)[source]¶
Decode a bit string from contents octets.
- Parameters:
data (
memoryview|bytes) – Contents octets with leading unused-bits count byte.- Return type:
- Returns:
The decoded
BitString.- Raises:
ValueError – If data is empty.
- bac_py.encoding.primitives.encode_date(date)[source]¶
Encode a
BACnetDateto 4 bytes: year-1900, month, day, day-of-week.Valid years are 1900–2155 or
0xFF(unspecified).- Parameters:
date (
BACnetDate) – TheBACnetDateto encode.- Return type:
- Returns:
4 bytes representing the encoded date.
- Raises:
ValueError – If the year is outside the valid range.
- bac_py.encoding.primitives.decode_date(data)[source]¶
Decode a
BACnetDatefrom 4 bytes.- Parameters:
data (
memoryview|bytes) – At least 4 bytes of encoded date data.- Return type:
- Returns:
The decoded
BACnetDate.- Raises:
ValueError – If data contains fewer than 4 bytes.
- bac_py.encoding.primitives.encode_time(time)[source]¶
Encode a
BACnetTimeto 4 bytes: hour, minute, second, hundredth.- Parameters:
time (
BACnetTime) – TheBACnetTimeto encode.- Return type:
- Returns:
4 bytes representing the encoded time.
- bac_py.encoding.primitives.decode_time(data)[source]¶
Decode a
BACnetTimefrom 4 bytes.- Parameters:
data (
memoryview|bytes) – At least 4 bytes of encoded time data.- Return type:
- Returns:
The decoded
BACnetTime.- Raises:
ValueError – If data contains fewer than 4 bytes.
- bac_py.encoding.primitives.encode_object_identifier(obj_type, instance)[source]¶
Encode a BACnet object identifier to 4 bytes.
Object type is a 10-bit field (0–1023). Instance is a 22-bit field (0–4,194,303). Delegates to
ObjectIdentifierfor encoding.
- bac_py.encoding.primitives.decode_object_identifier(data)[source]¶
Decode a BACnet object identifier from 4 bytes.
- Parameters:
data (
memoryview|bytes) – At least 4 bytes of encoded object identifier data.- Return type:
- Returns:
Tuple of
(object_type, instance_number).- Raises:
ValueError – If data contains fewer than 4 bytes.
- bac_py.encoding.primitives.encode_boolean(value)[source]¶
Encode a boolean value as a single contents octet.
For context-tagged booleans. Application-tagged booleans encode the value in the tag itself.
- bac_py.encoding.primitives.decode_boolean(data)[source]¶
Decode a boolean value from a single contents octet.
- Parameters:
data (
memoryview|bytes) – At least 1 byte; the first byte is interpreted as the boolean value.- Return type:
- Returns:
Trueif the first byte is non-zero,Falseotherwise.- Raises:
ValueError – If data is empty.
- bac_py.encoding.primitives.encode_application_tagged(tag_number, data)[source]¶
Encode data with an application tag.
- bac_py.encoding.primitives.encode_context_tagged(tag_number, data)[source]¶
Encode data with a context-specific tag.
- bac_py.encoding.primitives.encode_application_null()[source]¶
Encode an application-tagged Null.
- Return type:
- Returns:
Application-tagged Null encoding (tag only, no content).
- bac_py.encoding.primitives.encode_application_boolean(value)[source]¶
Encode an application-tagged Boolean.
Per Clause 20.2.3, the value is encoded in the L/V/T bits of the tag with no contents octet.
- bac_py.encoding.primitives.encode_application_unsigned(value)[source]¶
Encode an application-tagged Unsigned Integer.
- bac_py.encoding.primitives.encode_application_signed(value)[source]¶
Encode an application-tagged Signed Integer.
- bac_py.encoding.primitives.encode_application_real(value)[source]¶
Encode an application-tagged Real.
- bac_py.encoding.primitives.encode_application_double(value)[source]¶
Encode an application-tagged Double.
- bac_py.encoding.primitives.encode_application_octet_string(value)[source]¶
Encode an application-tagged Octet String.
- bac_py.encoding.primitives.encode_application_character_string(value)[source]¶
Encode an application-tagged Character String.
- bac_py.encoding.primitives.encode_application_enumerated(value)[source]¶
Encode an application-tagged Enumerated.
- bac_py.encoding.primitives.encode_application_date(date)[source]¶
Encode an application-tagged Date.
- Parameters:
date (
BACnetDate) – TheBACnetDateto encode.- Return type:
- Returns:
Application-tagged Date encoding.
- bac_py.encoding.primitives.encode_application_time(time)[source]¶
Encode an application-tagged Time.
- Parameters:
time (
BACnetTime) – TheBACnetTimeto encode.- Return type:
- Returns:
Application-tagged Time encoding.
- bac_py.encoding.primitives.encode_application_object_id(obj_type, instance)[source]¶
Encode an application-tagged Object Identifier.
- bac_py.encoding.primitives.encode_context_object_id(tag_number, obj_id)[source]¶
Encode an
ObjectIdentifierwith a context-specific tag.- Parameters:
tag_number (
int) – Context tag number.obj_id (
ObjectIdentifier) – TheObjectIdentifierto encode.
- Return type:
- Returns:
Context-tagged Object Identifier encoding.
- bac_py.encoding.primitives.encode_context_unsigned(tag_number, value)[source]¶
Encode an unsigned integer with a context-specific tag.
- bac_py.encoding.primitives.encode_context_signed(tag_number, value)[source]¶
Encode a signed integer with a context-specific tag.
- bac_py.encoding.primitives.encode_context_enumerated(tag_number, value)[source]¶
Encode an enumerated value with a context-specific tag.
- bac_py.encoding.primitives.encode_context_boolean(tag_number, value)[source]¶
Encode a boolean with a context-specific tag.
Context-tagged booleans use a 1-byte contents octet, unlike application-tagged booleans which encode the value in the tag LVT.
- bac_py.encoding.primitives.encode_context_real(tag_number, value)[source]¶
Encode a Real with a context-specific tag.
- bac_py.encoding.primitives.encode_context_octet_string(tag_number, value)[source]¶
Encode an octet string with a context-specific tag.
- bac_py.encoding.primitives.encode_context_bit_string(tag_number, value)[source]¶
Encode a bit string with a context-specific tag.
- bac_py.encoding.primitives.encode_context_date(tag_number, value)[source]¶
Encode a date with a context-specific tag.
- Parameters:
tag_number (
int) – Context tag number.value (
BACnetDate) – TheBACnetDateto encode.
- Return type:
- Returns:
Context-tagged date encoding.
- bac_py.encoding.primitives.encode_application_bit_string(value)[source]¶
Encode an application-tagged Bit String.
- bac_py.encoding.primitives.decode_application_value(data)[source]¶
Decode application-tagged bytes to a native Python value.
Inspects the application tag number and dispatches to the appropriate decoder. Returns native Python types:
Tag 0 (Null) ->
NoneTag 1 (Boolean) ->boolTag 2 (Unsigned) ->intTag 3 (Signed) ->intTag 4 (Real) ->floatTag 5 (Double) ->floatTag 6 (Octet String) ->bytesTag 7 (Character String) ->strTag 8 (Bit String) ->BitStringTag 9 (Enumerated) ->intTag 10 (Date) ->BACnetDateTag 11 (Time) ->BACnetTimeTag 12 (Object Id) ->ObjectIdentifier- Parameters:
data (
bytes|memoryview) – Application-tagged encoded bytes.- Return type:
- Returns:
Decoded Python value.
- Raises:
ValueError – If the tag is not application-class or is unrecognised.
Example:
from bac_py.encoding.primitives import ( decode_application_value, encode_application_real, ) encoded = encode_application_real(72.5) value = decode_application_value(encoded) # -> 72.5
- bac_py.encoding.primitives.decode_all_application_values(data)[source]¶
Decode all application-tagged values from concatenated bytes.
Iterates through the buffer, decoding each application-tagged element and collecting them into a list.
- Parameters:
data (
bytes|memoryview) – Concatenated application-tagged encoded bytes.- Return type:
- Returns:
List of decoded Python values.
- Raises:
ValueError – If a non-application tag is encountered or the number of decoded values exceeds
_MAX_DECODED_VALUES.
- bac_py.encoding.primitives.decode_and_unwrap(data)[source]¶
Decode application-tagged bytes and unwrap single-element lists.
Convenience wrapper around
decode_all_application_values()that returns a single value directly when the data contains exactly one application-tagged element,Nonefor empty data, or the full list for multiple elements.- Parameters:
data (
bytes|memoryview) – Concatenated application-tagged encoded bytes.- Return type:
- Returns:
Noneif data decodes to zero elements, the single decoded value if exactly one element, or alistof decoded values if multiple elements.
- bac_py.encoding.primitives.encode_property_value(value, *, int_as_real=False)[source]¶
Encode a Python value to application-tagged bytes.
Handles the common types stored in BACnet object properties, including both primitive and constructed BACnet types.
- Parameters:
- Return type:
- Returns:
Application-tagged encoded bytes.
- Raises:
TypeError – If the value type is not supported.
APDU Encoding¶
APDU encoding and decoding per ASHRAE 135-2016 Clause 20.1.
- class bac_py.encoding.apdu.ConfirmedRequestPDU(segmented, more_follows, segmented_response_accepted, max_segments, max_apdu_length, invoke_id, sequence_number, proposed_window_size, service_choice, service_request)[source]¶
Bases:
objectBACnet Confirmed-Request PDU (Clause 20.1.2).
- Parameters:
- class bac_py.encoding.apdu.UnconfirmedRequestPDU(service_choice, service_request)[source]¶
Bases:
objectBACnet Unconfirmed-Request PDU (Clause 20.1.3).
- class bac_py.encoding.apdu.SimpleAckPDU(invoke_id, service_choice)[source]¶
Bases:
objectBACnet SimpleACK PDU (Clause 20.1.4).
- class bac_py.encoding.apdu.ComplexAckPDU(segmented, more_follows, invoke_id, sequence_number, proposed_window_size, service_choice, service_ack)[source]¶
Bases:
objectBACnet ComplexACK PDU (Clause 20.1.5).
- Parameters:
- class bac_py.encoding.apdu.SegmentAckPDU(negative_ack, sent_by_server, invoke_id, sequence_number, actual_window_size)[source]¶
Bases:
objectBACnet SegmentACK PDU (Clause 20.1.6).
- Parameters:
- class bac_py.encoding.apdu.ErrorPDU(invoke_id, service_choice, error_class, error_code, error_data=b'')[source]¶
Bases:
objectBACnet Error PDU (Clause 20.1.7).
Represents the error response with error-class, error-code, and optional trailing error data for extended error types (e.g. ChangeList-Error, CreateObject-Error).
- Parameters:
invoke_id (int)
service_choice (int)
error_class (ErrorClass)
error_code (ErrorCode)
error_data (bytes)
- error_class: ErrorClass¶
Error class categorising the error (e.g. object, property, resource).
- class bac_py.encoding.apdu.RejectPDU(invoke_id, reject_reason)[source]¶
Bases:
objectBACnet Reject PDU (Clause 20.1.8).
- Parameters:
invoke_id (int)
reject_reason (RejectReason)
- reject_reason: RejectReason¶
Reason the request was rejected.
- class bac_py.encoding.apdu.AbortPDU(sent_by_server, invoke_id, abort_reason)[source]¶
Bases:
objectBACnet Abort PDU (Clause 20.1.9).
- Parameters:
sent_by_server (bool)
invoke_id (int)
abort_reason (AbortReason)
- abort_reason: AbortReason¶
Reason the transaction was aborted.
- bac_py.encoding.apdu.encode_apdu(pdu)[source]¶
Encode an APDU dataclass to wire-format bytes.
Dispatches to the appropriate encoder based on the PDU type.
- Parameters:
pdu (
ConfirmedRequestPDU|UnconfirmedRequestPDU|SimpleAckPDU|ComplexAckPDU|SegmentAckPDU|ErrorPDU|RejectPDU|AbortPDU) – The PDU dataclass instance to encode.- Return type:
- Returns:
Encoded APDU bytes ready for transmission.
- Raises:
TypeError – If pdu is not a recognised PDU type.
- bac_py.encoding.apdu.decode_apdu(data)[source]¶
Decode an APDU from raw bytes.
Inspects the PDU type nibble in the first byte and dispatches to the appropriate decoder.
- Parameters:
data (
memoryview|bytes) – Raw APDU bytes.- Return type:
ConfirmedRequestPDU|UnconfirmedRequestPDU|SimpleAckPDU|ComplexAckPDU|SegmentAckPDU|ErrorPDU|RejectPDU|AbortPDU- Returns:
Decoded PDU dataclass instance.
- Raises:
ValueError – If data is too short to decode.
TypeError – If the PDU type is not recognised.