Skip to content

blackbull.mqtt.messages

blackbull.mqtt.messages

MQTT 5.0 control-packet codec — Sprint 52.

Level-A (pure-data) layer for the blackbull-mqtt broker sidecar: the 15 MQTT 5.0 control packets as frozen dataclasses, a wire encoder/decoder, the MQTT 5.0 property system, reason codes, and the topic-filter matching algorithm. No I/O and no broker state live here — that is the job of :mod:blackbull.mqtt.broker and :mod:blackbull.mqtt.connection.

MQTT Version 5.0, OASIS Standard

https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html

Decoder return contract

:func:decode_packet returns the decoded message object. Every message also unpacks into (message, bytes_consumed) so a caller walking a buffer of concatenated packets can advance its offset::

msg = decode_packet(buf)            # attribute access / isinstance
msg, consumed = decode_packet(buf)  # buffer-walking

This dual ergonomics is provided by :meth:MQTTMessage.__iter__; the consumed count is recorded on the instance during decode.

ConnectFlags

Bases: IntFlag

Single-bit flags in the CONNECT flags byte (§3.1.2.3).

The Will QoS field is the two-bit subfield at :data:WILL_QOS_SHIFT (mask :data:WILL_QOS_MASK), not a flag here.

IncompletePacket

Bases: Exception

The buffer does not yet hold a complete packet — read more bytes.

MQTTDecodeError

Bases: ValueError

A buffer could not be decoded as a valid MQTT control packet.

MQTTMessage

Base for all MQTT control-packet dataclasses.

Provides the dual decode contract: a decoded message unpacks into (message, bytes_consumed). The byte count is set by :func:decode_packet via :meth:_set_consumed; messages built by hand report 0.

MQTTPacketType

Bases: IntEnum

The 15 MQTT 5.0 control packet types (§2.1.1 Table 2-1).

MQTTReasonCode

Bases: int

An MQTT 5.0 reason code (§2.4).

A thin int subclass so any byte value (0-255) is representable without raising — undefined codes report name == 'Unknown'. Codes < 0x80 are success/normal; >= 0x80 are errors (§2.4).

PropertyInfo

Bases: NamedTuple

Static description of one MQTT 5.0 property identifier (§2.2.2.2).

ProtocolLevel

Bases: IntEnum

CONNECT Protocol Level (§3.1.2.2). This broker speaks V5_0.

PublishFlagBits

Bases: IntFlag

Single-bit flags in the PUBLISH fixed header (§3.3.1).

QoS is the two-bit subfield at :data:PUBLISH_QOS_SHIFT.

PublishFlags

Bases: NamedTuple

Decoded PUBLISH fixed-header flags (§3.3.1).

ReasonCode

Bases: IntEnum

The subset of §2.4 reason codes the broker references by name.

This is the single definition of these values; their human-readable names live once in :data:_REASON_CODE_NAMES (the full §2.4 registry used by :class:MQTTReasonCode). Importers (blackbull.mqtt.broker, blackbull.mqtt.connection) use these members instead of redeclaring raw hex, so a code can never drift between modules.

SubscriptionOptions

Bases: IntFlag

Single-bit options in the SUBSCRIBE options byte (§3.8.3.1).

decode_packet(data)

Decode the first MQTT control packet in data.

Returns the message; it also unpacks into (message, bytes_consumed). Raises :class:IncompletePacket if the buffer is short, or :class:MQTTDecodeError if the bytes are not a valid packet.

decode_properties(data, offset=0)

§2.2.2 — Decode a properties block; return (props, consumed).

consumed counts the Property Length prefix plus the property bytes.

decode_publish_flags(flags_byte)

§3.3.1 — DUP (bit 3), QoS (bits 2-1), RETAIN (bit 0).

decode_variable_byte_integer(data)

§1.5.5 — Decode a Variable Byte Integer; return (value, consumed).

Trailing bytes beyond the integer are ignored (the caller tracks them).

encode_packet(message)

Serialize an MQTT control packet to its wire representation.

encode_properties(properties)

§2.2.2 — Encode a properties dict to Property Length + body.

encode_variable_byte_integer(value)

§1.5.5 — Encode an int (0..268,435,455) as a Variable Byte Integer.

extract_flags(first_byte)

§2.1.1 — Flags occupy bits 3-0 of the first fixed-header byte.

extract_packet_type(first_byte)

§2.1.1 — Packet type is bits 7-4 of the first fixed-header byte.

Type 0 is Reserved/forbidden (§2.1.1 Table 2-1) and raises ValueError.

get_property_info(identifier)

Return the :class:PropertyInfo for a property identifier, or None.

topic_matches_filter(topic, filter_str)

§4.7 — Return True if topic matches subscription filter_str.

Handles + (single level), # (multi level, terminal), the $ leading-character rule (§4.7.2), and $share/<group>/<filter> shared subscriptions (§4.8.2).

validate_topic_filter(filter_str)

§4.7.1 — Validate a subscription Topic Filter.

Returns True when valid; raises :class:ValueError describing the first rule violated. Enforces single-# / terminal-# / whole-level wildcard rules (§4.7.1.2-3) and the $share share-name rule (§4.8.2).

validate_topic_name(topic)

§4.7.1 — A Topic Name (used in PUBLISH) is literal: non-empty, no wildcards (+/#) and no null character. Leading/trailing slashes are permitted (they denote zero-length levels).