Skip to main content

Python client for the AccordionQ2 Hardware Management REST API

Project description

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

Using uv (recommended)

# 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 six groups, each covering one area of the hardware API. 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]}")

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

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.

Using uv (recommended)

# 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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

accordionq2-5.13.0.tar.gz (114.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

accordionq2-5.13.0-py3-none-any.whl (27.4 kB view details)

Uploaded Python 3

File details

Details for the file accordionq2-5.13.0.tar.gz.

File metadata

  • Download URL: accordionq2-5.13.0.tar.gz
  • Upload date:
  • Size: 114.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for accordionq2-5.13.0.tar.gz
Algorithm Hash digest
SHA256 6bf9bb0908fef9787d1d6152f99008dd76f57ed90ffdc52a27c3a197f9ae3f4d
MD5 b92f789662bf4e6d75126c049676761a
BLAKE2b-256 d7ce45cf8f9926cd04bfedd226288c3df7b853f03149677a07956a995c663b9a

See more details on using hashes here.

Provenance

The following attestation bundles were made for accordionq2-5.13.0.tar.gz:

Publisher: ci.yml on esharpab/esharp-accordion-projects-webapiclient-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file accordionq2-5.13.0-py3-none-any.whl.

File metadata

  • Download URL: accordionq2-5.13.0-py3-none-any.whl
  • Upload date:
  • Size: 27.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for accordionq2-5.13.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cd4dd2518626869e159185ee269f505063ed6efcb2ff1385eed708e038ff51d3
MD5 0ffb3c2d5137e43eba2eeb497655c44b
BLAKE2b-256 82a0921451324c09b760beac1e60f40ab76cd9cdf675893c6aeea21250dc7d31

See more details on using hashes here.

Provenance

The following attestation bundles were made for accordionq2-5.13.0-py3-none-any.whl:

Publisher: ci.yml on esharpab/esharp-accordion-projects-webapiclient-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page