Skip to content

Controller

The high-level entry point. Wraps ComApClient and caches the ConfigurationTable on connect, enabling name-based lookup and transparent password elevation.

pycomap.Controller

Controller(
    client: ComApClient[TransportT],
    access_code: str,
    password: int | None = None,
    include_invisible: bool = False,
)

High-level async client for a ComAp controller.

Fetches and caches the ConfigurationTable on connect, enabling name-based lookup for all subsequent calls. Password elevation for write-protected setpoints is handled automatically (lazy, on first protected write) when password is provided.

Parameters:

Name Type Description Default
client ComApClient[TransportT]

A ComApClient wrapping any transport. The Controller takes ownership of the connection lifecycle — do not call client.connect() yourself.

required
access_code str

The controller's AccessCode (base/anonymous read-only code, often "0"). Drives ECDH/AES key derivation — not the write password.

required
password int | None

The write-protection password (integer 0-9999). Required for setpoints with access_level > 0; omit to operate in read-only mode.

None
include_invisible bool

When False (default), ONE_TIME values in the 'Invisible' group are skipped during connect, saving one read_object round-trip per invisible item. Set to True to cache them anyway.

False

Examples:

Read-only access::

async with Controller(
    ComApClient(EthernetTransport(IPv4Address("192.168.1.9"))), access_code="0"
) as ctrl:
    values = await ctrl.read_values()

With write access::

async with Controller(
    ComApClient(EthernetTransport(IPv4Address("192.168.1.9"))),
    access_code="0",
    password=1234,
) as ctrl:
    await ctrl.set_setpoint("Nominal RPM", 1500)

client property

client: ComApClient[TransportT]

The underlying low-level client (escape hatch for direct comm object access).

table property

table: ConfigurationTable

The cached ConfigurationTable.

Available after connect.

values property

values: list[ValueDescription]

All value descriptions from the cached ConfigurationTable.

setpoints property

setpoints: list[SetpointDescription]

All setpoint descriptions from the cached ConfigurationTable.

one_time_values property

one_time_values: Mapping[int, Value]

Static ONE_TIME values read once at connect time.

Returns a read-only {comm_object_number: value} mapping for every ONE_TIME value successfully read during connect. Entries use the same type and resolution rules as read_values. Empty before connect; stable for the lifetime of the connection.

timezone property

timezone: timezone

Controller's configured UTC offset, derived from the Time Zone setpoint (C.O. 24366) read at connect time.

Derived by parsing the Time Zone setpoint's GMT label (e.g. 'GMT+2:00' for EET) and adding one hour when Summer Time Mode is 'Summer' or 'Summer-S'. Supports half-hour offsets. Call refresh_timezone to re-read after a setpoint change.

summer_time_mode property

summer_time_mode: int

Raw value of the Summer Time Mode setpoint (8727) read at connect time.

Known values: 0 = Winter, 2 = Summer, 4 = Summer-S. Call refresh_timezone to re-read after a change.

connect async

connect() -> None

Open the transport, authenticate, and fetch the ConfigurationTable.

close async

close() -> None

Close the underlying transport.

value_info

value_info(name_or_number: str | int) -> ValueDescription

Look up a value description by name or comm object number.

Parameters:

Name Type Description Default
name_or_number str | int

Exact value name (e.g. "RPM") or comm object number.

required

Returns:

Type Description
ValueDescription

The matching ValueDescription.

Raises:

Type Description
KeyError

If no value with that name or number exists.

ComApProtocolError

If the name is shared by multiple values — use a number instead.

setpoint_info

setpoint_info(
    name_or_number: str | int,
) -> SetpointDescription

Look up a setpoint description by name or comm object number.

Parameters:

Name Type Description Default
name_or_number str | int

Exact setpoint name (e.g. "Nominal RPM") or comm object number.

required

Returns:

Type Description
SetpointDescription

The matching SetpointDescription.

Raises:

Type Description
KeyError

If no setpoint with that name or number exists.

ComApProtocolError

If the name is shared by multiple setpoints — use a number instead.

value_options

value_options(
    name_or_number: str | int,
) -> list[tuple[int, str]]

Return the available options for a STRING_LIST value.

Options are stored in CommonNames at indices [low_limit .. high_limit]. The wire value (0-based) is what the controller sends on the wire and what value_label expects; the label is the string shown on the front panel and in InteliConfig. Note that read_values resolves STRING_LIST values to their label automatically — this method is for enumerating all possible options up front (e.g. to build a legend or validate against known states).

Parameters:

Name Type Description Default
name_or_number str | int

Value name or comm object number.

required

Returns:

Type Description
list[tuple[int, str]]

[(wire_value, label), ...] ordered by wire value.

Raises:

Type Description
ComApProtocolError

If the value is not STRING_LIST type.

Examples:

>>> ctrl.value_options("Engine State")
[(0, 'Ready'), (1, 'Prestart'), (2, 'Cranking'), ...]

setpoint_options

setpoint_options(
    name_or_number: str | int,
) -> list[tuple[int, str]]

Return the available options for a STRING_LIST setpoint.

Options are stored in CommonNames at indices [low_limit .. high_limit]. The wire value (0-based) is what you pass to set_setpoint; the label is the string shown on the front panel and in InteliConfig.

Parameters:

Name Type Description Default
name_or_number str | int

Setpoint name or comm object number.

required

Returns:

Type Description
list[tuple[int, str]]

[(wire_value, label), ...] ordered by wire value.

Raises:

Type Description
ComApProtocolError

If the setpoint is not STRING_LIST type.

Examples:

>>> ctrl.setpoint_options("Summer Time Mode")
[(0, 'Disabled'), (1, 'Winter'), (2, 'Summer'), (3, 'Winter-S'), (4, 'Summer-S')]

value_label

value_label(
    name_or_number: str | int, wire_value: int
) -> str

Return the display label for a STRING_LIST value's wire integer.

Label = CommonNames[low_limit + wire_value]. Note that read_values resolves STRING_LIST values automatically — this method is for cases where you have a raw wire integer and need the label.

Parameters:

Name Type Description Default
name_or_number str | int

Value name or comm object number.

required
wire_value int

Raw 0-based wire integer (as returned by the controller).

required

Returns:

Type Description
str

Human-readable label string, or str(wire_value) if out of range.

Raises:

Type Description
ComApProtocolError

If the value is not STRING_LIST type.

value_bit_names

value_bit_names(
    name_or_number: str | int,
) -> list[tuple[int, str]]

Return the bit labels for a BINARY* value.

Labels come from CommonNames starting at bit_name_index; bits without a name are omitted from the result.

Parameters:

Name Type Description Default
name_or_number str | int

Value name or comm object number.

required

Returns:

Type Description
list[tuple[int, str]]

[(bit_index, label), ...], bit 0 = LSB, ascending order.

Raises:

Type Description
ComApProtocolError

If the value is not a BINARY* type or has no bit labels.

read_values async

read_values() -> dict[int, Value]

Read all values from ValuesAll (C.O. 24560).

STRING_LIST values are resolved to their display label. Text-typed values (SHORT_STRING, IP_ADDRESS, etc.) are decoded to ASCII str. All other values are int, float, or raw bytes (binary, domain, timer types).

Returns:

Type Description
dict[int, Value]

{comm_object_number: value} for every value whose data fits within the

dict[int, Value]

ValuesAll blob, including static ONE_TIME values (firmware version,

dict[int, Value]

ID string, etc.).

Examples:

>>> values = await ctrl.read_values()
>>> rpm_num = ctrl.value_info("RPM").number
>>> values[rpm_num]
1450

read_setpoints async

read_setpoints() -> dict[int, Value]

Read all setpoints from SetpointsAll (C.O. 24559).

STRING_LIST setpoints are resolved to their display label, matching what set_setpoint accepts for a clean read-modify-write round-trip. Text-typed setpoints are decoded to ASCII str.

Returns:

Type Description
dict[int, Value]

{comm_object_number: value} for every setpoint in the controller's table.

read_states async

read_states() -> dict[int, ValueState]

Read all value protection states (ValueStatesAll, C.O. 24555).

read_alarms async

read_alarms() -> list[AlarmRecord]

Read the current alarm list (AlarmList, C.O. 24545).

read_history async

read_history(count: int = 10) -> list[HistoryRecord]

Read up to count of the most recent history records, newest first.

decode_history_snapshot

decode_history_snapshot(
    record: HistoryRecord,
) -> dict[int, Value]

Decode the value snapshot embedded in an alarm HistoryRecord.

Alarm/event records carry a snapshot of the ValuesAll blob captured at the moment of the event. Returns {number: decoded_value} for every value whose data fits within the snapshot; the set of values is typically the first ~31 entries from the controller's value table (those with small data_index values).

Returns an empty dict for text records (record.is_text=True) or records with no embedded data.

Use value_info to look up a number's name and metadata::

snapshot = ctrl.decode_history_snapshot(rec)
for number, val in snapshot.items():
    info = ctrl.value_info(number)
    print(f"{info.name}: {val}")

read_value async

read_value(name_or_number: str | int) -> Value

Read a single value by name or number.

Reads ValuesAll internally — use read_values when you need multiple values to avoid redundant round-trips. For ONE_TIME static values use one_time_value or one_time_values.

Parameters:

Name Type Description Default
name_or_number str | int

Value name or comm object number.

required

Returns:

Type Description
Value

Decoded value; same type rules as read_values.

Raises:

Type Description
KeyError

If no value with that name or number exists.

ComApProtocolError

If the name is shared by multiple values, or if the value is ONE_TIME category — use one_time_value() instead.

one_time_value

one_time_value(name_or_number: str | int) -> Value

Return a cached ONE_TIME value by name or number (no network call).

ONE_TIME values are read once at connect time and stored in one_time_values.

Parameters:

Name Type Description Default
name_or_number str | int

Value name or comm object number.

required

Returns:

Type Description
Value

Resolved value; strings are already null-stripped.

Raises:

Type Description
KeyError

If no value with that name or number exists in the table.

ComApProtocolError

If the name is shared by multiple values, if the value is not ONE_TIME category, or if it was not cached (e.g. skipped as invisible or failed to read at connect).

read_setpoint async

read_setpoint(name_or_number: str | int) -> Value

Read a single setpoint by name or number (one round-trip).

Parameters:

Name Type Description Default
name_or_number str | int

Setpoint name or comm object number.

required

Returns:

Type Description
Value

Decoded value; same type rules as read_setpoints.

Raises:

Type Description
KeyError

If no setpoint with that name or number exists.

set_setpoint async

set_setpoint(
    name_or_number: str | int, value: Value
) -> None

Write a setpoint by name or number.

Parameters:

Name Type Description Default
name_or_number str | int

Setpoint name or comm object number.

required
value Value

New value. Type depends on the setpoint's DataType:

  • Numeric (UNSIGNED*, INTEGER*, FLOAT, BINARY*): pass int or float; decimal_places scaling is applied automatically. Range-checked against low_limit/high_limit.
  • STRING_LIST: pass a str label (e.g. "Winter") or an int wire index. Labels are resolved via setpoint_options.
  • CHAR: pass an int.
  • Text (SHORT_STRING, IP_ADDRESS, etc.): pass a str; ASCII-encoded, zero-padded or truncated to the wire field length.
  • Any type: pass bytes to write the raw wire value directly (skips validation).
required

Raises:

Type Description
ComApAuthError

If the setpoint requires a password and none was supplied at construction, or if the password is rejected / locked out.

ValueError

If value is out of the setpoint's valid range.

Examples:

>>> await ctrl.set_setpoint("Nominal RPM", 1500)
>>> await ctrl.set_setpoint("Summer Time Mode", "Winter")
>>> await ctrl.set_setpoint("IP Address", "192.168.1.10")

execute_command async

execute_command(command: ControllerCommand) -> int

Execute a controller command.

Parameters:

Name Type Description Default
command ControllerCommand

A ControllerCommand instance; use the Command enum for named commands (e.g. Command.FAULT_RESET).

required

Returns:

Type Description
int

Raw integer result code returned by the controller.

refresh_timezone async

refresh_timezone() -> None

Re-read the Time Zone and Summer Time Mode setpoints and update the cached timezone and summer_time_mode values.

Both setpoints are looked up by name from the cached ConfigurationTable, so no comm object numbers are hardcoded. Call this if the setpoints were changed while the Controller is connected.

read_datetime async

read_datetime() -> datetime.datetime | None

Read the controller's current clock as a naive datetime (local wall-clock time). Use read_aware_datetime to get a timezone-aware result.

read_aware_datetime async

read_aware_datetime() -> datetime.datetime | None

Read the controller's current clock as a timezone-aware datetime.

Combines the naive clock reading with the timezone cached at connect time (derived from the Time Zone setpoint 24366). Returns None if the controller's clock is invalid/unset.

sync_time async

sync_time(tz: tzinfo | None = None) -> None

Sync the controller clock to the current UTC time.

Requires write access — elevates automatically if a password was supplied.

Parameters:

Name Type Description Default
tz tzinfo | None

Timezone for the local time written to the controller.

  • None (default): uses timezone — the UTC offset read from the controller's own Time Zone setpoint at connect time.
  • Any datetime.tzinfo (pytz, zoneinfo, or datetime.timezone): overrides the controller's setting. Use when the setpoint is not yet configured::

    import pytz await ctrl.sync_time(tz=pytz.timezone("Europe/Kiev"))

None

Raises:

Type Description
ComApAuthError

If write access is needed but no password was provided.

elevate_access async

elevate_access() -> None

Send the write-protection password to the controller, enabling protected writes.

Idempotent — safe to call multiple times; the password is only sent once. Call this early to validate the password without waiting for the first write.

Raises:

Type Description
ComApAuthError

If no password was supplied to the Controller.

ComApInvalidPasswordError

If the controller rejects the password.