Skip to main content

sismatic

A blocking Python facade for driving Extron devices over SSH using their Simple Instruction Set (SIS). The name is SIS + automatic: it handles the SIS machinery — connection pooling, the SSH handshake, byte-level framing — behind the scenes so you address a pool of devices by id and send plain instructions.

The package is a thin Python layer over a compiled Rust extension (sismatic-core), shipped as an abi3 wheel that covers CPython 3.9+ with a single build.

Install

pip install sismatic

Quickstart

from sismatic import Sis

# No connections are opened here; devices connect lazily on first use.
sis = Sis.from_file("devices.toml")

for device_id in sorted(sis.ids()):
    print(device_id)

sis.query("atrium-101", "firmware")          # read a built-in field
sis.register("atrium-101", "title", "Wk 4")  # write a metadata register
sis.command("atrium-101", "start")           # run a recorder command

Use optionally as a context manager to control the teardown of the session deterministically:

with Sis.from_file("devices.toml") as sis:
    sis.command("atrium-101", "start")
# every SSH connection is closed on exit

Configuration

from_file

from_file picks the deserializer from the extension (.toml, .json, .yaml/.yml). A config is a defaults table plus a list of devices:

TOML:

[defaults]
port = 22023
connect_secs = 5
exchange_secs = 3
eager = true              # open connections up front instead of on first use
sis_keepalive_secs = 120  # while warm, probe idle devices so connections stay warm
eager_retry_secs = 30     # while cold, retry connecting to unreachable eager devices

[[device]]
id = "atrium-101"
host = "10.0.0.7"
username = "admin"
password = "extron"

[[device]]
id = "annex-far"
host = "10.0.0.8"
username = "admin"
password = "extron"
connect_secs = 10  # per-device override
exchange_secs = 8

YAML:

---
defaults:
  username: "joe"
  password: "schmoe"
  eager: true
  sis_keepalive_secs: 120
  eager_retry_secs: 30

devices:
  - id: Hall A
    host: 10.0.150.1
  - id: Hall B
    host: 10.0.150.2
  - id: Hall C
    host: 10.0.150.3

Device groups

A [[group]] names one or more devices so they act as one — the case being more than one recorder in a physical room that must start together. Addressing the group id sends the instruction to every member at once, over the members' own warm connections:

[[device]]
id = "room-5-front"
host = "10.0.0.7"

[[device]]
id = "room-5-back"
host = "10.0.0.8"

[[group]]
id = "room-5"
devices = ["room-5-front", "room-5-back"]

A group id is accepted anywhere a device id is. Because a group has many members, a group call returns a dict keyed by member id (a single device still returns its scalar value):

sis.command("room-5", "start")
# {'room-5-front': 'RcdrY1', 'room-5-back': 'RcdrY1'}

sis.groups()          # -> ['room-5']

Each member must name a device defined in the same config, and group ids share the device id namespace (a group may not reuse a device's id). If any member fails, the call raises, naming which members failed.

from_config

Not using a file? Sis.from_config(mapping) takes an already-parsed dict shaped the same way, so you own the parsing. You can have a source config in any format like INI, XML, a database row, or environment variables with any parser that can produce a dictionary in the expected format.

Minimal Example with a configuration literal (no additonal parsing):

from sismatic import Sis
import logging
logging.basicConfig(level=logging.DEBUG)

devices = {
  "defaults": {
    "username": "joe",
    "password": "schmoe",
    "eager": True,
    "sis_keepalive_secs": 120,
    "eager_retry_secs": 30,
    "port": 22023,
  },
  "devices": [
      {"id": "Hall A", "host": "https://10.0.150.1"},
      {"id": "Hall B", "host": "https://10.0.150.2"},
  ],
}
sis = Sis.from_config(devices)
print(sis.ids())

Connections

Connections are lazy by default — the expensive SSH handshake is paid on a device's first instruction. Set eager to pay it up front, and sis_keepalive_secs to send a benign probe on an interval so a device's inactivity timer never lets the connection go cold. Because eager means "hold a warm connection over time," eager_retry_secs sets how often to re-attempt the handshake for an eager device that is unreachable at startup or has since dropped (0 gives up after the first failure). See the design note on eager connections and SIS keepalive for the full rationale.

API at a glance

>>> from sismatic import Sis
>>> [m for m in dir(Sis) if not m.startswith('_')]
['close', 'command', 'from_config', 'from_file', 'from_toml', 'groups', 'ids', 'query', 'register']

The wheel ships a PEP 561 py.typed marker and a type stub, so editors and mypy see full signatures, including the exact accepted instruction names. Full API docs: https://metzenseifner.github.io/sismatic/.

Example: start a recording across a batch of devices

# control_recording.py
# Starts recording and stamps a title across a batch of devices.

from dataclasses import dataclass
from sismatic import Sis

@dataclass(frozen=True)
class RecordingJob:
    """(device_id × title) as a product type. A job is exactly a pairing of
    the two — never one without the other — so the type says that, instead
    of the two strings being threaded separately through every call site as
    two positional arguments that happen to always travel together."""
    device_id: str
    title: str


def run_job(sis: Sis, job: RecordingJob) -> None:
    sis.register(job.device_id, "title", job.title)  # "title" — see Register::Title
    sis.command(job.device_id, "start")              # "start" — see Command::Start


def main() -> None:
    with Sis.from_file("devices.toml") as sis:
        jobs = [
            RecordingJob(device_id="atrium-101", title="Week 4 — Lecture"),
            RecordingJob(device_id="annex-far", title="Week 4 — Overflow Room"),
        ]
        for job in jobs:
            run_job(sis, job)


if __name__ == "__main__":
    main()

License

ECL-2.0. Part of the sismatic workspace.

Metadata

Release files for sismatic 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sismatic 0.6.0
File Size Uploaded
sismatic-0.6.0.tar.gz 127.5 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for sismatic 0.6.0
File Interpreter ABI Platform
sismatic-0.6.0-cp39-abi3-manylinux_2_28_x86_64.whl CPython 3.9 abi3 Linux glibc 2.28+ x86-64 Details
sismatic-0.6.0-cp39-abi3-manylinux_2_28_aarch64.whl CPython 3.9 abi3 Linux glibc 2.28+ ARM64 Details
sismatic-0.6.0-cp39-abi3-macosx_11_0_arm64.whl CPython 3.9 abi3 macOS 11.0+ ARM64 Details

Total release size: 7.2 MB

Release files / sismatic-0.6.0.tar.gz

Download URL sismatic-0.6.0.tar.gz
Size 127.5 kB
Tags Source
SHA-256 checksum
How to use checksums
93408e20e22e8ea1a5edfa07d6b14dbc6a18e63bad9e340d617131051699bfa2
BLAKE2b-256 checksum
How to use checksums
0a0e796a28045e44fe905e6f23801042ba15a7a0f9ef9e6b0c53e25c9ed3a8f6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Release files / sismatic-0.6.0-cp39-abi3-manylinux_2_28_x86_64.whl

Download URL sismatic-0.6.0-cp39-abi3-manylinux_2_28_x86_64.whl
Size 2.3 MB
Tags CPython 3.9 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
adc372a4423b52908653bd1a481805736a999cab7fd866f0926fed6bbf501ac3
BLAKE2b-256 checksum
How to use checksums
9200aedc97425de7c935037897cbe41dd714e4695fddf3ee34b10cbc2c710f6e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Release files / sismatic-0.6.0-cp39-abi3-manylinux_2_28_aarch64.whl

Download URL sismatic-0.6.0-cp39-abi3-manylinux_2_28_aarch64.whl
Size 2.2 MB
Tags CPython 3.9 Linux glibc 2.28+ ARM64 abi3
SHA-256 checksum
How to use checksums
6c587e93b95c855d069c8a8396fa006a5725ae40d8991c5191810eb5b8107266
BLAKE2b-256 checksum
How to use checksums
94596b7b04c9214c3066576eb1264c2c974a3f3423409a138a3d2c38bf0f1b57
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Release files / sismatic-0.6.0-cp39-abi3-macosx_11_0_arm64.whl

Download URL sismatic-0.6.0-cp39-abi3-macosx_11_0_arm64.whl
Size 2.6 MB
Tags CPython 3.9 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
4a18bada88ba9e5f5a6cdbbb69b205c21fde4bf9e24b4643fe04cf7a7f337121
BLAKE2b-256 checksum
How to use checksums
b7bc1ee0f8debe14f9904082e8290783d9568a876460997e2211cd26ae63c06c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Release history Release notifications | RSS feed

0.7.3

4 release files

0.7.2

4 release files

0.7.1

4 release files

0.7.0

4 release files

0.6.1

4 release files

This release

0.6.0 This release

4 release files

0.4.1

4 release files

0.4.0

4 release files

0.3.1

4 release files

0.3.0

4 release files

0.2.25

4 release files

0.2.24

4 release files

0.2.19

4 release files

0.2.18

4 release files

0.2.17

4 release files

0.2.15

4 release files

0.1.15

4 release files

0.1.13

4 release files

0.1.9

4 release files

0.1.8

4 release files

0.1.7

4 release files

0.1.6

4 release files

0.1.5

4 release files

0.1.4

4 release files

0.1.3

4 release files

0.1.2

4 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page