Documentation

Commands

Commands travel from a Control Panel to a PD. In Python each command is a frozen dataclass in the osdp.commands module. You construct one with keyword arguments and hand it to ControlPanel.submit_command:

from osdp import commands, BuzzerControlCode

cp.submit_command(101, commands.Buzzer(
    reader=0,
    control_code=BuzzerControlCode.DefaultTone,
    on_count=10,
    off_count=10,
    rep_count=3,
))

Every field is validated in the constructor, so an out-of-range value or an over-long buffer raises immediately, at the line where you built the command, rather than being truncated on its way into a packet. Byte and string fields have documented maximum lengths (commands.MAX_TEXT_LEN, MAX_MFG_DATA_LEN, and so on) sourced from the C library.

A PD may also return a command from its command handler to answer a query inline — most commonly a Status reply to a status request.

Reader output

Output

Drive a digital output line. Its control_code is an OutputControlCode:

classOutput

Set the state of an output line.

Example:

>>> cmd = Output(output_no=0, control_code=OutputControlCode.TemporaryOn,
...              timer_count=30)
>>> cmd.timer_count
30
Fields
int
Zero-based output line number.
OutputControlCode
What to do to the output line.
enumOutputControlCode

What an `Output` command does to the output line.

Values
Nop
Do nothing.
PermanentOff
Drive the output to its inactive state permanently.
PermanentOn
Drive the output to its active state permanently.
PermanentOffAllowTimed
Go permanently inactive, but let a running timer finish first.
PermanentOnAllowTimed
Go permanently active, but let a running timer finish first.
TemporaryOn
Go active for timer_count, then revert.
TemporaryOff
Go inactive for timer_count, then revert.

LED

Control a reader LED. An LED command carries a temporary block, a permanent block, or both; each is described by the helper dataclasses below. Counts are in units of 100ms, and within a block on_count and off_count cannot both be zero.

classLED

Control a reader LED.

An LED carries two independent parameter blocks. Setting only `permanent` changes the LED until something else changes it; setting only `temporary` overrides it for a while. Setting both at once is how a temporary state is cancelled and a new permanent state applied in the same command.

A block whose control code is Nop does nothing, so it is normalised to None; passing neither block is an error.

Example:

Flash red for two seconds, then go back to whatever it was:

>>> cmd = LED(temporary=TemporaryLEDParams(on_color=LEDColor.Red,
...                                        on_count=5, off_count=5,
...                                        timer_count=20))
>>> cmd.permanent is None
True

Cancel a running temporary state and go steady green, in one command:

>>> cmd = LED(
...     temporary=TemporaryLEDParams(
...         control_code=TemporaryLEDControlCode.Cancel),
...     permanent=PermanentLEDParams(on_color=LEDColor.Green,
...                                  on_count=1),
... )
>>> cmd.temporary.control_code.name
'Cancel'
Fields
int
Zero-based reader number.
None
Temporary state to apply, or None to leave it alone.
classTemporaryLEDParams

The temporary half of an LED command.

A temporary state runs for `timer_count` and then gives way to whatever permanent state the LED was in.

Example:

>>> params = TemporaryLEDParams(on_color=LEDColor.Red, on_count=10,
...                             off_count=10, timer_count=20)
>>> params.on_color.name
'Red'
Fields
TemporaryLEDControlCode
Whether to set the temporary state or cancel a running one.
int
Duration of the ON phase of a blink, in units of 100ms.
LEDColor
Color shown during the ON phase.
control_code
classPermanentLEDParams

The permanent half of an LED command.

A permanent state persists until it is changed. It has no timer; that is what makes it permanent.

Example:

A steady green: light up, and never blink off.

>>> params = PermanentLEDParams(on_color=LEDColor.Green, on_count=1)
>>> params.control_code.name
'Set'

Both durations zero is rejected; the PD would refuse such a command.

>>> PermanentLEDParams(on_color=LEDColor.Green)
Traceback (most recent call last):
ValueError: PermanentLEDParams: on_count and off_count cannot both be zero when the control code is Set. For a steady color use on_count=1, off_count=0
Fields
PermanentLEDControlCode
Whether to apply the permanent state.
int
Duration of the ON phase of a blink, in units of 100ms.
LEDColor
Color shown during the ON phase.
control_code

Each block's control code selects whether it takes effect; colors are LEDColor values:

enumTemporaryLEDControlCode

What the temporary block of an `LED` command does.

Values
Nop
Do nothing; equivalent to omitting the temporary block.
Cancel
Cancel any running temporary state and show the permanent state now.
Set
Apply the temporary state and start its timer.
enumPermanentLEDControlCode

What the permanent block of an `LED` command does.

Values
Nop
Do nothing; equivalent to omitting the permanent block.
Set
Apply the permanent state.
enumLEDColor

Colors a reader LED can be set to.

Values
Black
No color; the LED is off.
Red
Green
Amber
Blue
Magenta
Cyan
White

Buzzer

Sound the reader's buzzer. Its control_code is a BuzzerControlCode:

classBuzzer

Control a reader's audible output.

Example:

>>> cmd = Buzzer(control_code=BuzzerControlCode.DefaultTone,
...              on_count=2, off_count=2, rep_count=3)
>>> cmd.rep_count
3
Fields
int
Zero-based reader number.
BuzzerControlCode
Whether to sound the tone or silence it.
enumBuzzerControlCode

What a `Buzzer` command does.

Values
NoTone
Do nothing.
Off
Silence the buzzer.
DefaultTone
Sound the reader's default tone.

Text

Show text on a reader's display. Its control_code is a TextControlCode:

classText

Show a message on a reader's display.

Example:

>>> cmd = Text(control_code=TextControlCode.PermanentNoWrap,
...            data="PLEASE WAIT")
>>> cmd.offset_row
1
Fields
int
Zero-based reader number.
TextControlCode
How long the message stays up, and whether it wraps.
str
The message.
enumTextControlCode

How a `Text` command displays its message.

Values
PermanentNoWrap
PermanentWrap
TemporaryNoWrap
Display for temp_time seconds, then revert.
TemporaryWrap
Display for temp_time seconds, then revert.

Configuration

Comset / ComsetDone

Change a PD's address and baud rate. A PD acknowledges by returning a ComsetDone from its command handler.

classComset

Change the PD's address and baud rate.

The PD applies the change after replying, so the CP must reconfigure its own channel to match or it will lose the device.

Example:

>>> cmd = Comset(address=42, baud_rate=115200)
>>> cmd.address
42
Fields
int
The PD's new address.
classComsetDone

Tells a PD app that a Comset it received has taken effect.

Delivered to a PD's command handler by the library. Apps never submit this.

Example:

>>> cmd = ComsetDone(address=42, baud_rate=115200)
>>> cmd.baud_rate
115200
Fields
int
The address the PD now answers on.

Keyset

Set the Secure Channel Base Key (SCBK) on a PD. See Secure Channel for how keying and install mode work.

classKeyset

Install a new secure channel base key on the PD.

The PD stores the key and uses it for every subsequent secure channel handshake, so losing it means losing the device.

Example:

>>> cmd = Keyset(data=bytes(range(16)))
>>> cmd.type
1
Fields
int
Key type.
bytes
The key itself.

Data and status

Manufacturer

Send a vendor-specific payload.

classManufacturer

Send a vendor-specific command.

A PD may answer inline by returning a `events.ManufacturerReply`, `events.ManufacturerStatus` or `events.ManufacturerError` from its command handler.

Example:

>>> cmd = Manufacturer(vendor_code=0x00030201, data=b"\\x01\\x02")
>>> cmd.data
b'\\x01\\x02'
Fields
int
The vendor's 3-byte IEEE OUI.
bytes
Vendor-defined payload, at most 64 bytes.

File Transfer

Begin or cancel a file transfer to the PD. Register a FileOps first so the library can read the file. The flags field is a FileTxFlag:

classFileTransfer

Start, or cancel, a file transfer to the PD.

The file's contents come from the file ops registered with `register_file_ops()`; this command only starts the process.

Example:

>>> cmd = FileTransfer(id=1)
>>> cancel = FileTransfer(id=1, flags=FileTxFlag.Cancel)
>>> bool(cancel.flags & FileTxFlag.Cancel)
True
Fields
int
The file id, agreed out of band between the CP and the PD.
FileTxFlag
Set Cancel to abort a transfer that is already running.
enumFileTxFlag

Flags for a `FileTransfer` command.

Values
Cancel
Abort an in-progress transfer instead of starting one.

Smartcard

Talk to a smartcard attached to the PD. PivData retrieves a PIV object; GenAuth and CrAuth run a cryptographic challenge against the card. The payloads travel as multi-part messages and the PD answers with the matching events.PivData / events.GenAuth / events.CrAuth carrying the reassembled reply. See Smartcard Commands for the full flow.

classPivData

Retrieve the contents of a PIV object from the PD's smartcard.

The PD answers with an `events.PivData` carrying the reassembled object data; replies larger than one OSDP packet travel as multi-part messages transparently.

Example:

>>> cmd = PivData(oid=b"\\x5f\\xc1\\x02", element=7)
>>> cmd.oid
b'_\\xc1\\x02'
Fields
bytes
3-byte PIV Object ID per NIST SP 800-73-4 Part 1.
int
PIV element ID within the object.
classGenAuth

Direct a general-authenticate challenge to the PD's smartcard.

The challenge travels as a multi-part message; the PD answers with an `events.GenAuth` carrying the reassembled response.

Example:

>>> cmd = GenAuth(algorithm=0xA7, key=0x9E, data=b"challenge")
>>> len(cmd.data)
9
Fields
int
Selected algorithm per ISO 7816-4:2005 7.5.5.
bytes
Cryptographic challenge payload; non-empty.
classCrAuth

Direct a challenge/response sequence to the PD's smartcard.

Identical shape to `GenAuth`; the PD answers with an `events.CrAuth`.

Fields
int
Selected algorithm per ISO 7816-4:2005 7.5.5.
bytes
Cryptographic challenge payload; non-empty.

Status

Request a status report, or (from a PD) carry one back. The type is a StatusReportType:

classStatus

Query the PD's status, or a PD's answer to that query.

A CP submits this with an empty `report` to ask. A PD answers by returning a Status with one report byte per entry from its command handler.

Example:

>>> query = Status(type=StatusReportType.Input)
>>> query.report
b''

A PD answering with three input contacts, the middle one active:

>>> reply = Status(type=StatusReportType.Input, report=bytes([0, 1, 0]))
>>> len(reply.report)
3
Fields
StatusReportType
Which set of status bits this concerns.
bytes
One byte per entry.
enumStatusReportType

Which set of status bits a status report carries.

Values
Input
One entry per input (contact) line.
Output
One entry per output line.
Local
Local status: two entries, report[0] tamper and report[1] power.
Reader
Reader tamper status: one entry per reader (0 normal, 1 not connected, 2 tamper).

Notification

Library-generated notification delivered as a command on the PD side when notifications are enabled. The type is a NotificationType:

classNotification

A library notification delivered to a PD app.

Produced by the library, not by a peer, when `LibFlag.EnableNotification` is set. Apps never submit this. The CP-side equivalent is `events.Notification`, which has the same shape.

Example:

>>> note = Notification(type=NotificationType.PeripheralDeviceStatus,
...                     online=True)
>>> note.type.name
'PeripheralDeviceStatus'
Fields
NotificationType
What happened.
int
Which command completed (CommandId).
bool
Whether that command succeeded.
enumNotificationType

Kind of library notification.

Each type carries its own typed fields on `osdp.events.Notification` / `osdp.commands.Notification`; this discriminator selects which are valid.

Values
Command
A command completed.
SecureChannelStatus
Secure channel changed.
PeripheralDeviceStatus
PD reachability changed.
MultipartStart
MultipartProgress
MultipartDone
PdId
PD identity collected (CP mode).

Biometrics

BioRead / BioMatch

Ask a reader to capture a biometric (BioRead) or match one against a supplied template (BioMatch). type is a BioType and format a BioFormat. A captured template larger than one packet is only carried when both roles opt in to multi-part biometric replies:

classBioRead

Ask a reader to capture a biometric and return the template.

The PD answers with an `events.BioRead`.

Example:

>>> cmd = BioRead(type=BioType.RightThumbPrint,
...               format=BioFormat.AnsiIncits378, quality=200)
>>> cmd.type.name
'RightThumbPrint'
Fields
int
Zero-based reader number.
BioType
Which biometric to capture.
BioFormat
The template encoding to return.
classBioMatch

Ask a reader to scan a biometric and match it against a template.

The PD answers with an `events.BioMatch` carrying the match score.

Example:

>>> cmd = BioMatch(type=BioType.RightThumbPrint,
...                format=BioFormat.AnsiIncits378, quality=200,
...                data=b"template")
>>> len(cmd.data)
8
Fields
int
Zero-based reader number.
BioType
Which biometric to scan.
BioFormat
The encoding of the template in data.
bytes
The template to match against, at most 128 bytes.
enumBioType

Which biometric a reader should capture or match.

Values
NotSpecified
RightThumbPrint
RightIndexFingerPrint
RightMiddleFingerPrint
RightRingFingerPrint
RightLittleFingerPrint
LeftThumbPrint
LeftIndexFingerPrint
LeftMiddleFingerPrint
LeftRingFingerPrint
LeftLittleFingerPrint
RightIrisScan
RightRetinaScan
LeftIrisScan
LeftRetinaScan
FullFaceImage
RightHandGeometry
LeftHandGeometry
enumBioFormat

Encoding of a biometric template.

Values
NotSpecified
RawPGM
AnsiIncits378

See also