Documentation

Control Panel

A Control Panel (CP) is the bus master: it polls one or more Peripheral Devices, sends them commands, and receives their events. In Python that role is the ControlPanel class. Each managed PD is described by a PDInfo and addressed by its bus address throughout the API.

Lifecycle

A CP moves through the same phases every time:

  1. ConstructControlPanel(pd_info_list, ...), optionally passing an event handler and a command-completion handler up front.
  2. Register handlers (optional) — or set them later with set_event_handler / set_command_completion_handler.
  3. Startstart() spawns a background thread that services the bus. This is what satisfies OSDP's requirement to refresh the library at least every 50ms; your application never has to poll on a timer.
  4. Wait for the linkonline_wait_all() / sc_wait_all() block until the PDs are online and (if keyed) the secure channel is established.
  5. Run — submit commands with submit_command() and drain events with get_event() (or a registered handler).
  6. Tear downteardown() stops the thread and releases the context.
cp = ControlPanel(pd_info, log_level=LogLevel.Debug)
cp.start()
cp.sc_wait_all()
...
cp.teardown()
Callbacks run on the library thread

Event and command-completion handlers are invoked from the background refresh thread, not the thread that called start(). Guard any state they share with your application, and keep them short — blocking in a handler stalls the bus. The queue-based get_event() is the alternative when you would rather pull events on your own thread.

ControlPanel

classControlPanel

Manages one or more PDs over a shared channel.

Runs a background thread that services the bus; nothing happens until `start()` is called.

Example:

cp = ControlPanel([PDInfo(address=101, channel=my_channel)])
cp.start()
cp.online_wait(101)
cp.submit_command(101, commands.Buzzer(rep_count=3))
Methods
__init__(self, list[PDInfo] pd_info_list, LogLevel log_level=LogLevel.Info, EventHandler|None event_handler=None, CommandCompletionHandler|None command_completion_handler=None)
ctx(self) -> "_sys.ControlPanel"

The library context. Raises once this CP has been torn down.

set_event_handler(self, EventHandler|None handler)

Install the handler called for every event from every PD.

set_command_completion_handler(self, CommandCompletionHandler|None handler)

Install the handler called when a submitted command settles.

submit_command(self, int address, Command command) -> bool

Queue a command for a PD. Returns False if it could not be queued.

The command is copied on the way in, so it may be discarded or reused as soon as this returns.

flush_commands(self, int address) -> int

Drop every queued command for a PD. Returns how many were dropped.

Each one is still reported to the completion handler, as Flushed.

get_event(self, int address, float timeout=5) -> Event|None

Pop the next event from a PD, or None if none arrives in time.

A negative timeout does not block.

status(self) -> int

Bitmask of which PDs are online, indexed by their position.

is_online(self, int address) -> bool

Whether a PD is answering.

get_num_online(self) -> int

How many PDs are answering.

sc_status(self) -> int

Bitmask of which PDs have a secure channel up.

is_sc_active(self, int address) -> bool

Whether a PD's secure channel is up.

get_num_sc_active(self) -> int

How many PDs have a secure channel up.

get_pd_id(self, int address) -> PdId

The identity a PD reported during setup.

check_capability(self, int address, Capability cap) -> tuple[int, int]

A PD's (compliance_level, num_items) for one capability.

Both are zero when the PD does not claim the capability.

set_flag(self, int address, LibFlag flag) -> bool

Turn a per-PD flag on.

clear_flag(self, int address, LibFlag flag) -> bool

Turn a per-PD flag off.

enable_pd(self, int address) -> bool

Resume polling a PD.

disable_pd(self, int address) -> bool

Stop polling a PD without tearing down the CP.

is_pd_enabled(self, int address) -> bool

Whether a PD is being polled.

register_file_ops(self, int address, FileOps fops) -> bool

Supply the file a subsequent FileTransfer command will send.

get_file_tx_status(self, int address) -> FileTxStatus|None

How far the running transfer has got, or None if none is running.

get_metrics(self, int address) -> Metrics|None

Packet and command counters for a PD.

start(self)

Start servicing the bus.

stop(self)

Stop servicing the bus.

teardown(self)

Stop the bus and release the library context.

The CP cannot be used afterwards.

online_wait(self, int address, float timeout=8) -> bool

Block until a PD comes online, or the timeout elapses.

online_wait_all(self, float timeout=10) -> bool

Block until every PD is online, or the timeout elapses.

offline_wait(self, int address, float timeout=8) -> bool

Block until a PD goes offline, or the timeout elapses.

sc_wait(self, int address, float timeout=5) -> bool

Block until a PD's secure channel comes up.

sc_wait_all(self, float timeout=5) -> bool

Block until every PD's secure channel comes up.

Events

Events are generated by a PD and delivered to the CP. You can consume them two ways, and may mix them:

  • Pull — call get_event(address) in your own loop; it returns the next queued event or None.
  • Push — register an event handler (constructor argument or set_event_handler) that the library calls as events arrive.
def on_event(address: int, event) -> int:
    match event:
        case events.CardRead(data=data):
            print(f"PD {address}: card {data.hex()}")
    return 0

cp = ControlPanel(pd_info, event_handler=on_event)

The event payload types are documented on the Events page.

Command completion

Every command you submit settles exactly once. A command-completion handler is told the outcome — acknowledged, failed, flushed before it reached the wire, or aborted at teardown — as a CompletionStatus. The library hands your handler its own re-marshalled copy of the original command, so you can tell which one completed.

from osdp import CompletionStatus

def on_command_complete(address, command, status):
    if status == CompletionStatus.Ok:
        print(f"{type(command).__name__} completed on PD {address}")

cp.set_command_completion_handler(on_command_complete)

Commands

Build a command dataclass and submit it to a PD by address. See Commands for every command type and its fields.

cp.submit_command(101, commands.Buzzer(reader=0, control_code=BuzzerControlCode.DefaultTone))

flush_commands(address) drops any commands still queued for a PD; each dropped command settles with CompletionStatus.Flushed.

Capabilities, IDs, and flags

get_pd_id(address) returns the PD's PdId, and check_capability(address, capability) reports whether a PD advertises a given Capability and at what compliance level. set_flag / clear_flag toggle per-PD LibFlag options at runtime.

File transfer

To send a file to a PD, register a FileOps implementation with register_file_ops(address, fops), then submit a commands.FileTransfer. Progress is readable via get_file_tx_status(address).

See also