Documentation

API Reference

Everything documented here is importable from the top-level osdp package. This page covers the supporting types shared by both modes — how you describe a PD, carry bytes over the wire, key a secure channel, transfer files, and handle errors — plus the enumerations used throughout. The two device classes and the message payloads have their own pages:

Describing a PD

PDInfo

PDInfo is the descriptor for one peripheral device — its bus address, the Channel it is reached over, its secure-channel key, and its identity. A CP is constructed from a list of these; a PD from a single one. Constructing it with scbk=None selects install mode.

classPDInfo

Everything needed to talk to (or be) one PD.

A CP builds one of these per PD it manages; a PD builds one for itself. Only the CP's first PDInfo supplies the channel, since all PDs on an RS-485 bus share it.

Example:

>>> info = PDInfo(address=101, channel=None,
...               flags=[LibFlag.EnforceSecure])
>>> info.name
'PD-101'
Fields
int
The PD's address on the bus.
Channel
The transport to reach it over.
None
The 16-byte secure channel base key, or None to run without one.
str
A label for logs.
default_factory
Per-PD behaviour flags.
PdId
The identity this PD reports.
name

PdId

The PD's identity block — vendor, model, and firmware — reported by a PD and read back on a CP with get_pd_id.

classPdId

A PD's identity, as reported in its ID response.

Example:

>>> pd_id = PdId(version=1, model=1, vendor_code=0xCAFEBABE,
...              serial_number=0xDEADBEAF, firmware_version=0xDEADDEAD)
>>> pd_id.model
1
Fields
int
The PD's hardware version.

The capabilities model

A PD advertises what it can do as a set of capabilities. Each capability pairs a Capability function code with a compliance level and a number of items (for example, how many LEDs or output lines it has). You declare them when constructing a PeripheralDevice; a CP reads them back with check_capability.

PDCapabilities is the collection you pass in. It accepts either PDCapability instances or plain (Capability, compliance_level, num_items) tuples:

from osdp import Capability, PDCapabilities

pd_cap = PDCapabilities([
    (Capability.OutputControl, 1, 1),
    (Capability.LEDControl, 2, 1),
    (Capability.AudibleControl, 1, 1),
])
classPDCapability

One thing a PD claims to support.

Example:

>>> cap = PDCapability(function_code=Capability.OutputControl,
...                    compliance_level=1, num_items=8)
>>> cap.num_items
8
Fields
Capability
Which capability this describes.
int
How completely the PD implements it; the meaning is per-capability.

See PD Capabilities for the protocol-level meaning of each function code and its compliance levels.

Channel

LibOSDP does not own the transport. You subclass Channel and implement three methods that move bytes over your wire — serial, TCP, or anything else — and hand an instance to a PDInfo.

classChannel

A byte stream between a CP and one or more PDs.

Subclass this to talk over a serial port, a socket, or anything else. On an RS-485 bus every PD shares one channel, so reads may return bytes meant for another device; the library sorts that out.

The library calls these from its refresh thread, so they must not block for long: a PD has to be serviced at least every 50ms to stay in spec.

Methods
read(self, int max_bytes) -> bytes

Return up to `max_bytes` bytes, or b"" if none are waiting.

Must not block waiting for data to arrive.

write(self, bytes buf) -> int

Send as much of `buf` as possible; return how much was sent.

flush(self)

Discard anything buffered. May do nothing if that has no meaning.

A minimal pyserial implementation is shown in Getting Started.

KeyStore

KeyStore is a helper for generating and persisting Secure Channel Base Keys during development and testing. gen_key() returns a fresh random 16-byte key suitable for a PDInfo.

classKeyStore

Generates secure channel keys and remembers them across runs.

Keys live in memory until `commit_key()` writes them out. Without a directory they go to a temporary one that is removed when this object is, which is what tests want.

Example:

>>> store = KeyStore()
>>> key = store.new_key("my-pd")
>>> len(key)
16
>>> store.get_key("my-pd") == key
True
Fields
key_dir
temp_dir
Methods
__init__(self, str|None dir=None)
key_file(self, str name) -> str

Where the key called `name` is stored.

get_key(self, str name) -> bytes

Return a key held in memory. Raises KeyError if there is none.

new_key(self, str name, int key_len=KEY_LEN, bool force=True) -> bytes

Generate a key and hold it under `name`.

Raises KeyError if a key of that name exists and `force` is False.

update_key(self, str name, bytes key)

Replace the key held under `name`.

commit_key(self, str name)

Write the key held under `name` to disk.

load_key(self, str name, int key_len=KEY_LEN) -> bytes

Read a previously committed key back from disk.

gen_key(int key_len=KEY_LEN) -> bytes

Generate a new random key.

File transfer

To transfer a file to a PD, implement the FileOps protocol and register it with register_file_ops, then submit a commands.FileTransfer. FileTxStatus reports progress.

classFileOps

What a file transfer source or sink must implement.

Pass an object satisfying this to `register_file_ops()`. The library calls `open()` once, then `read()` or `write()` until the transfer finishes, then `close()`.

Methods
open(self, int file_id, int size) -> int

Begin a transfer. Return the file's size in bytes, or -1 to refuse.

read(self, int size, int offset) -> bytes

Return at most `size` bytes starting at `offset`.

write(self, bytes data, int offset) -> int

Store `data` at `offset`. Return the number of bytes stored.

close(self, int file_id) -> int

End the transfer. Return 0 on success.

classFileTxStatus

How far a file transfer has got.

Example:

>>> status = FileTxStatus(size=1024, offset=512)
>>> status.offset
512
Fields
int
Total size of the file, in bytes.

Metrics

Metrics is a snapshot of a link's counters — packets, errors, secure-channel handshakes, and command/event totals — read with get_metrics.

classMetrics

Counters the library keeps for one PD.

Fields
int
Packets put on the wire.

Errors

The bindings raise a small exception hierarchy, all subclasses of OSDPError. Raise NakError from a PD command handler to decline a command with a NakCode; MarshalError signals a command or event that could not cross the C boundary.

classOSDPError

Base class for every error this package raises.

classNakError

Raised by a PD command handler to reject a command.

The library turns this into a NAK reply to the CP. Raising it is the only way for a handler to decline a command; returning normally acknowledges it.

Example:

>>> raise NakError(NakCode.BioType)
Traceback (most recent call last):
osdp.errors.NakError: Command rejected: BioType
Fields
code
Why the command was rejected.
Methods
__init__(self, NakCode code=NakCode.Record)
classMarshalError

Raised when a command or event cannot cross the C boundary.

This means a malformed object was submitted, or the library produced something this version of the bindings does not understand. Either way it is a bug rather than a runtime condition to be handled.

Enumerations

These enums are shared across the API. The command- and event-specific enums (control codes, colors, card and biometric formats) are documented alongside the command or event that uses them on the Commands and Events pages.

Enum members take their values from the C library, so they cannot drift from it. The wire-facing ones tolerate values a peer sends that the spec does not name, surfacing them as UNKNOWN_0x.. members rather than raising mid-callback.

Capability

enumCapability

Things a PD can tell a CP it supports.

Values
Unused
ContactStatusMonitoring
OutputControl
CardDataFormat
LEDControl
AudibleControl
TextOutput
TimeKeeping
CheckCharacter
CommunicationSecurity
ReceiveBufferSize
CombinedMessageSize
SmartCard
Readers
Biometrics
SecurePinEntry
OSDPVersion

LogLevel

enumLogLevel

Verbosity of the library's logger.

Values
Emergency
Alert
Critical
Error
Warning
Notice
Info
Debug

LibFlag

enumLibFlag

Per-PD flags passed to `PDInfo`.

Values
EnforceSecure
Only allow communication in secure channel.
InstallMode
Allow a PD to receive a new secure channel key over a plaintext link.
IgnoreUnsolicited
Discard unsolicited replies from the PD.
EnableNotification
Deliver library notifications as events (CP) or commands (PD).
CapturePackets
Capture packets to a PCAP file for offline analysis.
AllowEmptyEncryptedDataBlock
Tolerate peers that send an encrypted data block with no payload.
BioReadrMultipart
Transfer biometric read replies larger than one packet in multiple parts.

CompletionStatus

enumCompletionStatus

Fate of a submitted command or event.

Every accepted command or event is reported exactly once, including those that were flushed or torn down before they reached the wire.

Values
Ok
Delivered to the peer and acknowledged.
Failed
Sent but the peer rejected it, or it could not be sent.
Flushed
Dropped from the queue before being sent.
Aborted
The context was torn down while it was still queued.

NakCode

enumNakCode

Reasons a PD command handler may reject a command.

Raise `osdp.NakError` with one of these from a command handler; the other NAK codes in the spec are produced by the library itself.

Values
BioType
The requested biometric type is not supported.
BioFormat
The requested biometric format is not supported.
Record
The command could not be processed.

See also