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:
ControlPanel— the CP mode class.PeripheralDevice— the PD mode class.- Commands and Events — the message dataclasses.
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.
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'intChannelNonestrdefault_factoryPdIdnamePdId
The PD's identity block — vendor, model, and firmware — reported by a PD and read back on a CP with get_pd_id.
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
1intThe 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),
])
One thing a PD claims to support.
Example:
>>> cap = PDCapability(function_code=Capability.OutputControl,
... compliance_level=1, num_items=8)
>>> cap.num_items
8CapabilityintSee 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.
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.
read(self, int max_bytes) -> bytesReturn up to `max_bytes` bytes, or b"" if none are waiting.
Must not block waiting for data to arrive.
write(self, bytes buf) -> intSend 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.
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
Truekey_dirtemp_dir__init__(self, str|None dir=None)key_file(self, str name) -> strWhere the key called `name` is stored.
get_key(self, str name) -> bytesReturn a key held in memory. Raises KeyError if there is none.
new_key(self, str name, int key_len=KEY_LEN, bool force=True) -> bytesGenerate 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) -> bytesRead a previously committed key back from disk.
gen_key(int key_len=KEY_LEN) -> bytesGenerate 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.
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()`.
open(self, int file_id, int size) -> intBegin a transfer. Return the file's size in bytes, or -1 to refuse.
read(self, int size, int offset) -> bytesReturn at most `size` bytes starting at `offset`.
write(self, bytes data, int offset) -> intStore `data` at `offset`. Return the number of bytes stored.
close(self, int file_id) -> intEnd the transfer. Return 0 on success.
How far a file transfer has got.
Example:
>>> status = FileTxStatus(size=1024, offset=512)
>>> status.offset
512intMetrics
Metrics is a snapshot of a link's counters — packets, errors, secure-channel handshakes, and command/event totals — read with get_metrics.
Counters the library keeps for one PD.
intErrors
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.
Base class for every error this package raises.
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: BioTypecode__init__(self, NakCode code=NakCode.Record)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
Things a PD can tell a CP it supports.
UnusedContactStatusMonitoringOutputControlCardDataFormatLEDControlAudibleControlTextOutputTimeKeepingCheckCharacterCommunicationSecurityReceiveBufferSizeCombinedMessageSizeSmartCardReadersBiometricsSecurePinEntryOSDPVersionLogLevel
Verbosity of the library's logger.
EmergencyAlertCriticalErrorWarningNoticeInfoDebugLibFlag
Per-PD flags passed to `PDInfo`.
EnforceSecureInstallModeIgnoreUnsolicitedEnableNotificationCapturePacketsAllowEmptyEncryptedDataBlockBioReadrMultipartCompletionStatus
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.
OkFailedFlushedAbortedNakCode
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.
BioTypeBioFormatRecordSee also
- Getting Started — install and a first app.
- Control Panel / Peripheral Device — the mode classes.
- Commands / Events — the message payloads.