Documentation
Python Getting Started
The libosdp Python package wraps the LibOSDP C library so you can build and test OSDP devices without writing any C. Commands and events are typed dataclasses, so your editor completes the fields, a type checker catches a wrong one before you run, and an out-of-range value raises where you wrote it instead of being silently truncated into a packet.
Install
Pre-built wheels are published on PyPI for Linux, macOS, and Windows:
pip install libosdp
That is all most users need. If no wheel matches your platform, pip builds from source, which needs a C compiler — see Build and Install for the per-OS toolchain requirements and other install methods.
A channel to talk over
LibOSDP does not own the transport. You implement a Channel that moves bytes over your wire (serial, TCP, etc.) and hand it to the device. A pyserial-backed channel looks like this:
import serial
from osdp import Channel
class SerialChannel(Channel):
def __init__(self, device: str, speed: int = 115200):
self.dev = serial.Serial(device, speed, timeout=0)
def read(self, max_bytes: int) -> bytes:
return self.dev.read(max_bytes)
def write(self, data: bytes) -> int:
return self.dev.write(data)
def flush(self) -> None:
self.dev.flush()
def __del__(self):
self.dev.close()
Both snippets below use this SerialChannel (pip install pyserial).
Control Panel mode
A Control Panel (CP) drives one or more Peripheral Devices (PDs). Each PD is described by a PDInfo. start() spawns a background thread that services the bus for you, so the application never has to poll on a timer.
from osdp import ControlPanel, PDInfo, KeyStore, LogLevel, LEDColor, commands, events
channel = SerialChannel("/dev/ttyUSB0")
# KeyStore.gen_key() provisions a random Secure Channel Base Key (SCBK).
pd_info = [
PDInfo(101, channel, scbk=KeyStore.gen_key()),
]
cp = ControlPanel(pd_info, log_level=LogLevel.Debug)
cp.start()
cp.sc_wait_all() # block until the secure channel is up
# An LED command carries a temporary block, a permanent block, or both.
# Counts are in units of 100ms; on_count and off_count cannot both be zero.
led_cmd = commands.LED(
reader=1,
led_number=0,
permanent=commands.PermanentLEDParams(
on_color=LEDColor.Red,
off_color=LEDColor.Black,
on_count=10,
off_count=10,
),
)
while True:
# Each event is its own type, so match gives you the right fields for free.
match cp.get_event(pd_info[0].address):
case events.CardRead(data=data):
print(f"CP: card {data.hex()}")
case events.KeyPress(data=keys):
print(f"CP: keypad {keys!r}")
cp.submit_command(pd_info[0].address, led_cmd)
The full CP surface — every method, the event queue, and completion callbacks — is on the Control Panel page. The commands you submit are documented in Commands; the events you read in Events.
Peripheral Device mode
A Peripheral Device (PD) answers a CP. Describe it with PDInfo, declare its capabilities, and hand LibOSDP a command handler. Return None to accept a command, return a command to answer it inline, or raise NakError to decline it.
from osdp import (
Capability, Command, NakCode, NakError, PDCapabilities, PDInfo,
PeripheralDevice, StatusReportType, commands, events, CardFormat,
)
channel = SerialChannel("/dev/ttyUSB0")
# scbk=None puts the PD in install mode (it accepts a key from the CP).
pd_info = PDInfo(101, channel, scbk=None)
pd_cap = PDCapabilities([
(Capability.OutputControl, 1, 1),
(Capability.LEDControl, 2, 1),
])
def command_handler(cmd: Command) -> Command | None:
match cmd:
case commands.Status(type=report_type):
return commands.Status(type=report_type, report=read_inputs())
case commands.BioRead():
raise NakError(NakCode.BioType)
return None
pd = PeripheralDevice(pd_info, pd_cap, command_handler=command_handler)
pd.start()
pd.sc_wait()
# For the raw card formats the length is in BITS; this one is ASCII, so bytes.
card_event = events.CardRead(
reader_no=1,
direction=1,
format=CardFormat.ASCII,
data=bytes([9, 1, 9, 2, 6, 3, 1, 7, 7, 0]),
)
while True:
pd.submit_event(card_event)
The full PD surface and the command-handler contract are on the Peripheral Device page.
Full examples
Runnable command-line versions of both apps live in the LibOSDP repo:
Where to next
- Build and Install — from source, per-OS.
- Control Panel / Peripheral Device — the two modes in depth.
- Commands and Events — the message payloads.
- API Reference —
PDInfo, capabilities,Channel,KeyStore, errors, and enums.