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
command_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
command_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 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.2.23
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sismatic-0.2.23.tar.gz | 98.5 kB | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sismatic-0.2.23-cp39-abi3-manylinux_2_28_x86_64.whl | CPython 3.9 | abi3 | Linux glibc 2.28+ x86-64 | Details |
| sismatic-0.2.23-cp39-abi3-macosx_11_0_arm64.whl | CPython 3.9 | abi3 | macOS 11.0+ ARM64 | Details |
| sismatic-0.2.23-cp39-abi3-macosx_10_12_x86_64.whl | CPython 3.9 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 7.7 MB
Release files / sismatic-0.2.23.tar.gz
| Download URL | sismatic-0.2.23.tar.gz |
|---|---|
| Size | 98.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b231b17539a2452ad370300e391dea954abab7b44fdd0a1f95a5fc12ef6a1090
|
|
BLAKE2b-256 checksum How to use checksums |
cdcbc49953344f78caebbbd5c1ad1942180c0a95bb83d1fe845d40fb9a1d9e65
|
| 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 Aug 7, 2026.
Transparency logRelease files / sismatic-0.2.23-cp39-abi3-manylinux_2_28_x86_64.whl
| Download URL | sismatic-0.2.23-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 |
5f9d19a9ee3fe81c496c99b8a85aa1c3f58a8822c14caf7ea392f85d4bb1816f
|
|
BLAKE2b-256 checksum How to use checksums |
43b4cebf44dd41e36a3e70f29f476359a03892932078dba51dcee769c576d23b
|
| 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 Aug 7, 2026.
Transparency logRelease files / sismatic-0.2.23-cp39-abi3-macosx_11_0_arm64.whl
| Download URL | sismatic-0.2.23-cp39-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 2.5 MB |
| Tags | CPython 3.9 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
cf6799298e74fc31271a1d994173bbc8d4874ed25391003ec5bd84dcad673749
|
|
BLAKE2b-256 checksum How to use checksums |
75fede98b84fae1fa63c057d8ff73df6279e408a59ea8dbbed27f2ca3af37d65
|
| 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 Aug 7, 2026.
Transparency logRelease files / sismatic-0.2.23-cp39-abi3-macosx_10_12_x86_64.whl
| Download URL | sismatic-0.2.23-cp39-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 2.7 MB |
| Tags | CPython 3.9 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
3d13bead7ca1de9470434afa74eaf8dad20fe788963620c2802e253276673d6e
|
|
BLAKE2b-256 checksum How to use checksums |
e20b22b1d1b78447a153b9025831acaf564a9c547cdc29cc566731d128103a62
|
| 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 Aug 7, 2026.
Transparency log