Skip to main content

AccordionQ2 Python Client

Python client library for the AccordionQ2 Hardware Management REST API. This is the Python counterpart of the .NET WebApiClient; both libraries expose the same API surface so switching between them feels natural.

PyPI NuGet Documentation

Requirements

Requirement Details
Python 3.11 or later
Platform Windows, Linux (including ARM / Raspberry Pi), macOS
Architecture 32-bit and 64-bit
Dependencies None — stdlib only (http.client, json, ssl, threading, dataclasses)

Installation

Install from PyPI:

pip install accordionq2
# Install the package
uv add accordionq2

# Or install from a local checkout with dev dependencies
uv sync --dev

pip (local checkout)

pip install -e .

Quick Start

from accordionq2 import AccordionQ2Client

with AccordionQ2Client("http://agent64.local:5000") as client:
    # Check the hardware connection
    status = client.connection.get_status()
    print("Connected:", status.is_connected)

    # Read a sensor value
    temp = client.resources.get_value("TempRegulator.CPU_TEMP")
    print("CPU temperature:", temp)

    # List all channels
    for ch in client.channels.get_all():
        print(f"  {ch.alias}: type={ch.channel_type}, unit={ch.unit}")

The client can also be used without a with block:

client = AccordionQ2Client("http://agent64.local:5000")
names = client.resources.get_names()
client.close()

Constructor

AccordionQ2Client(base_url, timeout=30.0, auth=None, verify=True, default_headers=None)
Parameter Type Default Description
base_url str — Base URL of the AccordionQ2 WebApi, e.g. "http://raspberrypi:5000"
timeout float 30.0 HTTP request timeout in seconds
auth tuple[str, str] | None None Optional (username, password) for HTTP Basic Auth
verify bool | str True TLS certificate verification. False disables (useful for self-signed certs). A string is treated as a path to a CA bundle.
default_headers dict[str, str] | None None Additional headers merged into every request (e.g. {"X-Api-Key": "secret"})
# HTTP Basic Auth
client = AccordionQ2Client("https://device.local:5001", auth=("admin", "secret"))

# Self-signed certificate — disable TLS verification
client = AccordionQ2Client("https://device.local:5001", verify=False)

# API key header
client = AccordionQ2Client("http://device.local:5000",
                           default_headers={"X-Api-Key": "my-api-key"})

API Reference

The client exposes groups, each covering one area of the hardware API; the main ones are below, and the documentation covers them all, including calibration, instruments, events and subscriptions, and the audit log. All methods are synchronous and raise AccordionQ2ApiError on HTTP errors.

client.connection — Connection Status

Method Returns Description
get_status() ConnectionStatusDto Check whether the API is connected to the hardware manager.
status = client.connection.get_status()
if not status.is_connected:
    print("Error:", status.last_error)

client.resources — Resource Values

Resources represent readable/writable hardware values such as voltages, temperatures, and firmware revisions. They are identified by a dotted name string (e.g. "TempRegulator.CPU_TEMP").

Method Returns Description
get_names() list[str] List all available resource names.
get_value(name) str Read the current value of a single resource.
set_value(name, value) — Write a value to a single resource.
get_values(names) dict[str, str] Read multiple resources in one call.
set_values(resources) — Write multiple resources in one call.
transact(name, value) str Write then read (command/response pattern).
# Single read
voltage = client.resources.get_value("0.1.ESH10000158.MON_3V3")

# Batch read
values = client.resources.get_values([
    "TempRegulator.CPU_TEMP",
    "Engine.Uptime",
])
for name, val in values.items():
    print(f"{name} = {val}")

client.channels — Hardware Channels

Channels represent multi-purpose I/O pins (analog, digital, I2C, SPI, etc.).

Method Returns Description
get_all() list[ChannelDto] Return every configured channel.
get_channel(alias=, net_name=) ChannelDto Look up one channel by alias or net name.
configure(config) — Partial-update a single channel.
configure_many(configs) — Partial-update multiple channels.
from accordionq2.models import ChannelConfigRequest
from accordionq2.enums import ChannelTypes

# Look up by alias
ch = client.channels.get_channel(alias="0.1.ESH10000158.MON_3V3")
print(f"Type: {ch.channel_type}, Direction: {ch.direction}")

# Check flags
if ch.channel_type & ChannelTypes.ANALOG:
    print("This is an analog channel")

# Partial update (only the fields you set are changed)
client.channels.configure(ChannelConfigRequest(
    alias="0.1.ESH10000158.MON_3V3",
    description="Main 3.3 V rail monitor",
    unit="V",
))

client.modules — Module Management

Method Returns Description
get_all() list[ModuleSettingsDto] All modules (loaded and unloaded).
get_loaded() list[ModuleSettingsDto] Currently loaded modules only.
load(module) — Load a module.
unload(module) — Unload a module.
configure(module) — Update module configuration.
get_physical_system() PhysicalSystemDto Hardware topology (host, MAC, modules).
get_licensed_apps() list[AppLicenseDto] Licensed applications.
get_all_apps() list[AppLicenseDto] All applications.
system = client.modules.get_physical_system()
print(f"Host: {system.host}, MAC: {system.mac}")
for mod in system.modules:
    print(f"  Slot {mod.index}: {mod.name} ({mod.product_id})")

for m in client.modules.get_loaded():
    print(f"  {m.name} (enabled={m.enabled})")

client.application — Application Lifecycle

Method Returns Description
get_name() str Application module name.
get_identification() str Application identification string.
get_status() ModuleStatus Current status (enum).
reset() — Reset the application engine.
list_config_files() list[str] Available configuration files.
get_loaded_config_files() list[str] Currently loaded config files.
load_config_file(name) — Load a configuration file.
save_config_file(name) — Save configuration to a file.
download_config_file(name) bytes Download a config file as raw bytes.
upload_config_file(name, data) — Upload a config file (bytes).
delete_config_file(name) — Delete a config file from the device.
from accordionq2.enums import ModuleStatus

status = client.application.get_status()
if status == ModuleStatus.OK:
    print("Application is running normally")

# Configuration file round-trip
data = client.application.download_config_file("factory.cfg")
client.application.upload_config_file("factory_backup.cfg", data)

client.media — Media Files

Method Returns Description
list_files() list[str] List media files on the device.
download_file(name) bytes Download a media file.
upload_file(name, data) — Upload a media file (bytes).
delete_file(name) — Delete a media file.
for f in client.media.list_files():
    print(f)

# Download and save locally
content = client.media.download_file("waveform.bin")
with open("waveform.bin", "wb") as fp:
    fp.write(content)

client.comm — Raw Bus Transactions (I2C, UART, SPI, Socket)

All byte data is hex-encoded on the wire. The client handles encoding and decoding transparently — callers work with plain bytes objects. BusTransactionResponse.received is always bytes.

Method Returns Description
i2c(device_name, address, action, ...) BusTransactionResponse I2C bus transaction (Send, Receive, SendReceive, or Scan).
uart(device_name, action, ...) BusTransactionResponse UART transaction (Send, Receive, SendReceive, or ClearBuffers).
spi(device_name, action, ...) BusTransactionResponse SPI transaction (Send, Receive, or SendReceive).
socket(device_name, action, ...) BusTransactionResponse TCP socket transaction (Send, Receive, or SendReceive).

I2C parameters:

Parameter Type Description
device_name str Device name as registered in the hardware manager
address int I2C 7-bit device address (0–127)
action BusActions SEND, RECEIVE, SEND_RECEIVE, or SCAN
data_to_send bytes Bytes to transmit (required for Send/SendReceive)
number_of_bytes_to_receive int Expected byte count for Receive/SendReceive
max_retries int Retry limit on NAK (-1 = device default)
from accordionq2.enums import BusActions

# I2C: scan the bus for connected devices
resp = client.comm.i2c("0.ESH10000597.I2C00", address=0x00,
                       action=BusActions.SCAN)
for addr in resp.received:
    print(f"Found device at 0x{addr:02X}")

# I2C: write two bytes to address 0x50
client.comm.i2c("0.ESH10000597.I2C00", address=0x50,
                action=BusActions.SEND,
                data_to_send=bytes([0x00, 0x10]))

# I2C: read 4 bytes from address 0x50
resp = client.comm.i2c("0.ESH10000597.I2C00", address=0x50,
                       action=BusActions.RECEIVE,
                       number_of_bytes_to_receive=4)
print(resp.received.hex())  # e.g. "aabbccdd"

# UART: send a SCPI query and read the response
resp = client.comm.uart("MyUartDevice",
                        action=BusActions.SEND_RECEIVE,
                        data_to_send=b"*IDN?\n",
                        number_of_bytes_to_receive=64,
                        timeout_ms=2000)
print(resp.received.decode("ascii"))

# SPI: full-duplex transfer
resp = client.comm.spi("MySpiDevice",
                       action=BusActions.SEND_RECEIVE,
                       data_to_send=bytes([0xAA, 0xBB]),
                       number_of_bytes_to_receive=2)

# Socket: send a SCPI query over TCP
resp = client.comm.socket("MySocketDevice",
                          action=BusActions.SEND_RECEIVE,
                          host_name="192.168.1.10", port=5025,
                          data_to_send=b"*IDN?\n",
                          number_of_bytes_to_receive=64)

client.numeric_results — Fast Numeric Sampling

NumericResult channels perform high-speed acquisition on physical channels, computing summary statistics (mean, min, max, stdev) server-side.

Method Returns Description
get_channels() list[NumericResultChannelDto] All NumericResult channels with sampling capabilities.
get_targets(channel_net_name) list[str] Physical channels that a NumericResult channel can sample.
measure(channel, target, samples, reduced_set) NumericMeasureResultDto Trigger an acquisition (result cached server-side).
get_mean(channel_net_name) float Mean of the last measurement.
get_min(channel_net_name) float Minimum of the last measurement.
get_max(channel_net_name) float Maximum of the last measurement.
get_stdev(channel_net_name) float Standard deviation of the last measurement.
get_samples(channel_net_name) list[float] Raw sample array (only if reduced_set=False).

Typical workflow:

# 1. Discover available NumericResult channels
channels = client.numeric_results.get_channels()
for ch in channels:
    print(f"{ch.net_name} (rate={ch.sample_rate} Hz)")

# 2. Check what a channel can sample
targets = client.numeric_results.get_targets(channels[0].net_name)
print("Available targets:", targets)

# 3. Trigger acquisition (result cached server-side)
meta = client.numeric_results.measure(
    channels[0].net_name, targets[0],
    samples=1000, reduced_set=True)
print(f"Acquired {meta.sample_count} samples in {meta.duration}")

# 4. Fetch summary statistics
mean  = client.numeric_results.get_mean(channels[0].net_name)
stdev = client.numeric_results.get_stdev(channels[0].net_name)
print(f"Mean = {mean:.6f}, StdDev = {stdev:.6f}")

# 5. For raw samples, use reduced_set=False
meta = client.numeric_results.measure(
    channels[0].net_name, targets[0],
    samples=100, reduced_set=False)
samples = client.numeric_results.get_samples(channels[0].net_name)
print(f"First 5 samples: {samples[:5]}")

client.lease — Control Lease

One client at a time may change the station, typically a test station for the length of a sequence. While another client holds the lease, this client's changes and forced reads raise AccordionQ2ApiError with status 423; reads with max_age_ms and subscriptions keep working.

Method Returns Description
get() LeaseDto Who holds the lease, if anyone.
acquire(owner, ttl_ms=30000) LeaseDto Take the lease (409 while someone else holds it).
renew(ttl_ms=None) LeaseDto Renew the lease this client holds.
release() — Release it.
hold(owner, ttl_ms=30000) context manager Acquire, renew in the background, release after the block.
with client.lease.hold("TAT station 3"):
    client.resources.set_value("0.4.ESH10000662.VSET1", "5")

client.system — Services, Reboot, Clock and boot.config

Method Returns Description
get_services() list[ServiceStatus] The hardware app, the WebApi and the dashboard.
service_action(service_id, action) — start, stop, restart, enable or disable.
reboot() — Reboot the Pi.
get_clock() ClockStatus The Pi's clock.
set_clock(utc=None, force=False) ClockStatus Set it, to this computer's clock by default.
boot.get() BootConfig The hardware app's start-up configuration (boot.config).
boot.set_startup(enabled=None, alias_files=None) BootConfig Turn boot.config on or off, and/or replace the alias files.
boot.update(*, if_modified=None, ...) BootConfig Edit boot.config section by section.

boot.config changes apply at the hardware app's next start (alias files and modules also on a reset). if_modified gives 409 instead of overwriting another edit, and the Wi-Fi password is never returned.

from accordionq2 import BootAliasFile

client.system.boot.set_startup(enabled=True,
                               alias_files=[BootAliasFile("station.csv")])
client.system.set_clock()

client.files — Station Files

Files in the station's folders (config, alias, fsms, media, extensions, and the read-only logs and webapi-logs).

Method Returns Description
get_roots() list[FileRoot] The folders the file API reaches.
list(root, path="") FileListing What a folder holds.
download(root, path) bytes A file's bytes.
upload(root, path, data, overwrite=False) FileEntry Upload a file.
create_folder(root, path) — Create a folder.
move(root, source, target, overwrite=False) — Rename or move within the folder.
delete(root, path, recursive=False) — Delete a file or folder.
data = client.files.download("alias", "station.csv")
client.files.upload("alias", "station_backup.csv", data)

client.firmware — Firmware Updates

The station installs only releases signed by E-Sharp, checked on the station whichever way the package arrives, and nothing below 6.0.0. Installing restarts the hardware app and the WebApi; install() and install_package() keep polling until the update has ended.

Method Returns Description
get_state() FirmwareState Installed version, source, minimum version, last update.
get_releases(include_beta=False) FirmwareReleases Releases, newest first.
set_source(location) FirmwareSource An http(s) address or a folder on the Pi; None for the default.
start_update(version, include_beta=False) FirmwareUpdateStatus Start installing and return at once.
get_update() / get_update_log() FirmwareUpdateStatus / str How the update stands, and its log.
wait_for_update(timeout=900.0, poll=2.0) FirmwareUpdateStatus Wait until it has ended.
install(version, include_beta=False, timeout=900.0) FirmwareUpdateStatus Start and wait.
upload_package(package) FirmwareRelease Send a rel-x.y.z.zip from this computer.
install_package(package, timeout=900.0) FirmwareUpdateStatus Upload, install and wait (a station without internet).
delete_package(file_name) — Remove a package from the station's cache.
releases = client.firmware.get_releases()
newest = next(r for r in releases.releases if r.installable)
status = client.firmware.install(newest.version)
if status.state != "succeeded":
    print(status.state, status.message)
    print(client.firmware.get_update_log())

# A station without internet
client.firmware.install_package("C:/releases/rel-6.0.0.zip")

Models

All models live in accordionq2.models.

Class Description
ConnectionStatusDto is_connected (bool), last_error (str or None)
ChannelDto Full channel description (18 fields including type, direction, alias, unit, etc.)
ChannelConfigRequest Partial-update request — only non-None fields are applied
ChannelLookupRequest Identify a channel by alias or net_name
ModuleSettingsDto Module configuration (name, enabled, class_name, initial_data, etc.)
PhysicalSystemDto Hardware topology (host, mac, firmware, modules tuple)
PhysicalModuleDto One hardware slot (index, name, product_id, revision, serial_number)
AppLicenseDto License info (name, key, expires, type)
BusTransactionResponse device_name, action, received (bytes), number_of_bytes_received
NumericResultChannelDto net_name, alias, possible_target_names, sample_rate, default_samples
NumericMeasureResultDto channel_net_name, target_net_name, sample_count, sample_rate, reduced_set, started, stopped, duration

The lease, system, boot.config, files and firmware models (LeaseDto, ServiceStatus, ClockStatus, BootConfig, BootAliasFile, BootModule, BootAddress, BootService, BootWifi, FileRoot, FileListing, FileEntry, FirmwareState, FirmwareSource, FirmwareReleases, FirmwareRelease, FirmwareUpdateStatus) live in their groups' modules and are importable from the top-level accordionq2 package.

Response models provide a from_dict(data) class method; request models provide a to_dict() instance method.

All response models are immutable (frozen=True) — fields cannot be modified after construction. Create a new instance if you need a different value.

Enumerations

All enums live in accordionq2.enums.

Enum Type Values
ModuleStatus str, Enum UNKNOWN, OK, WARNING, ERROR, DISABLED
AppTypes str, Enum UNKNOWN, SOFTWARE_MODULE, HARDWARE_MODULE
MpioUsageTypes str, Enum UNDEFINED, HIDDEN_SYSTEM_CONTROL, READ_ONLY_SYSTEM_CONTROL, USER_ALLOCATABLE, BUS_SIGNAL
DirectionTypes IntFlag UNDEFINED, IN, OUT
BusActions str, Enum UNDEFINED, SEND, RECEIVE, SEND_RECEIVE, SCAN, BREAK, CLEAR_BUFFERS, RECONFIGURE
ChannelTypes IntFlag UNDEFINED, ANALOG, DIGITAL, TEMPERATURE, I2C, SPI, UART, and many more

IntFlag enums support bitwise operations:

from accordionq2.enums import ChannelTypes, DirectionTypes

combined = ChannelTypes.ANALOG | ChannelTypes.DIGITAL
if ch.channel_type & ChannelTypes.I2C:
    print("I2C channel")

if ch.direction & DirectionTypes.IN:
    print("Input capable")

Error Handling

All API errors raise AccordionQ2ApiError (importable from the top-level package). The exception carries the HTTP status code and the server's error message:

from accordionq2 import AccordionQ2ApiError

try:
    ch = client.channels.get_channel(alias="does.not.exist")
except AccordionQ2ApiError as e:
    print(f"HTTP {e.status_code}: {e}")

Running the Tests

The test suite uses pytest.

# Install dev dependencies and run all unit tests
uv sync --dev
uv run pytest tests/unit/ -v

Using pip

# Install with test dependencies
pip install -e ".[dev]"

Unit Tests (no hardware required)

pytest tests/unit/ -v

Unit tests use mocks and run entirely offline.

Integration Tests (live device required)

# Requires a live AccordionQ2 device
ACCORDIONQ2_API_URL=http://mydevice:5000 pytest tests/ -m integration -v

On Windows (PowerShell):

$env:ACCORDIONQ2_API_URL = "http://mydevice.local:5000"
pytest tests/ -m integration -v

Comparison with the .NET Client

Both clients expose the same API surface with idiomatic naming for their respective language:

Concept .NET (AccordionQ2Client) Python (AccordionQ2Client)
Lifecycle IDisposable / using Context manager / with
Methods GetValueAsync(name) get_value(name)
Concurrency async / await Synchronous (thread-safe)
Nullability string? None
Config ChannelConfigRequest.Enabled = true ChannelConfigRequest(enabled=True)
Enums ChannelTypes.Analog ChannelTypes.ANALOG
Errors AccordionQ2ApiException AccordionQ2ApiError
Dependencies Newtonsoft.Json None (stdlib only)
Install dotnet add package AccordionQ2.WebApiClient pip install accordionq2
Package NuGet PyPI

License

Proprietary — see your license agreement for details.

Metadata

Release files for accordionq2 5.28.1

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

Source distribution (sdist)

Source distribution for accordionq2 5.28.1
File Size Uploaded Provenance
accordionq2-5.28.1.tar.gz 153.7 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for accordionq2 5.28.1
File Interpreter ABI Platform
accordionq2-5.28.1-py3-none-any.whl Python 3 none any Details

Total release size: 201.4 kB

Release files / accordionq2-5.28.1.tar.gz

Download URL accordionq2-5.28.1.tar.gz
Size 153.7 kB
Tags Source
SHA-256 checksum
How to use checksums
5ce9a758ddc950da15b3d62bb237e72b77d457420394540af68957a363226601
BLAKE2b-256 checksum
How to use checksums
81482dcb29871af36700f804f91c5eec4136975a80c61f3878af4ff177b10339
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 Oct 3, 2026.

Transparency log

Release files / accordionq2-5.28.1-py3-none-any.whl

Download URL accordionq2-5.28.1-py3-none-any.whl
Size 47.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e9668180c8a0516553e8bcb96f6887e8d68c4066f85fb56e3c59f803ddc2b2fb
BLAKE2b-256 checksum
How to use checksums
74a92d35e8fa23654000c48031eec205733b1dbffb8c2004bb8878a20e6e636a
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 Oct 3, 2026.

Transparency log

Security for accordionq2 5.28.1

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for accordionq2-5.28.1.tar.gz

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 Oct 3, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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