Skip to content

Parameters

Watlow controllers expose every settable / readable value as a parameter identified by a numeric id (e.g. 4001 = process value, 7001 = setpoint, 8003 = heat algorithm). The same parameter is addressable on both wire protocols — Standard Bus reads it as (class, member, instance), Modbus RTU reads it as a register address. watlowlib carries a registry of every published EZ-ZONE PM parameter so callers can use a canonical name and the library does the protocol-specific addressing.

See Design §5a for the registry rationale.

The registry at a glance

from watlowlib import PARAMETERS

spec = PARAMETERS.resolve("setpoint")
print(spec.parameter_id, spec.data_type, spec.rwes, spec.safety)
# 7001 DataType.FLOAT RwesFlag.RWES SafetyTier.PERSISTENT

spec = PARAMETERS.resolve(4001)
print(spec.name, spec.relative_addr, spec.cls, spec.member)
# process_value 360 0x68 0x01

PARAMETERS is the default ParameterRegistry, loaded eagerly from the bundled JSON on import. resolve() accepts a canonical name, an alias, or a parameter id; unknown lookups raise WatlowValidationError.

ParameterSpec

Every row carries enough metadata to address the parameter on either protocol and validate user input before sending.

Field Notes
parameter_id Watlow parameter id (e.g. 4001).
name Canonical lowercase identifier (e.g. process_value).
aliases Frozenset of alternate names (e.g. pv, process_temp).
data_type FLOAT, S32, U32, U16, U8, STRING, or PACKED (enumeration).
rwes R, W, RW, RWE, or RWES.
safety Derived from rwes: RREAD_ONLY, anything else → PERSISTENT.
cls / member Std Bus selector (class id, member id).
default_instance / max_instance Std Bus instance bounds (1..N for multi-loop SKUs).
relative_addr / absolute_addr Modbus register addresses (relative is the per-block offset; absolute is the full address).
register_count How many 16-bit Modbus registers the parameter occupies.
word_order HIGH_LOW or LOW_HIGH for multi-register values; None defers to the client default.
range_min / range_max Soft bounds parsed from the EZ-ZONE register list (when machine-readable).
family_hints Advisory ControllerFamily priors.

The Std Bus selector and Modbus selector are both populated for every parameter. Which one is used at call time depends on the session's active protocol.

RWES → SafetyTier

The EZ-ZONE register list tags every parameter with an RWES flag that encodes both access and persistence:

Flag Meaning Tier
R read-only READ_ONLY
W write-only (rare; e.g. action triggers) PERSISTENT
RW runtime read/write, not EEPROM-backed PERSISTENT
RWE RW + persisted to EEPROM PERSISTENT
RWES RWE + included in the user save / restore set PERSISTENT

Anything that writes — even RW ("runtime only") — needs confirm=True at the facade. The library treats every write as a state change worth confirming. See Safety for the gate order and the rationale.

Resolving by alias

The bundled JSON ships with one canonical name per parameter, but common alternate names resolve through the alias table:

PARAMETERS.resolve("pv")              # → parameter 4001 (process_value)
PARAMETERS.resolve("set_point")       # → parameter 7001 (setpoint)
PARAMETERS.resolve("heat_algorithm")  # → parameter 8003 (heat_algorithm)

Aliases are case-insensitive. The full alias table lives in registry/aliases.py.

Multi-instance parameters

Per-loop parameters carry an instance selector — instance 1 is loop 1, instance 2 is loop 2, etc. The default is always 1; the registry's max_instance caps the upper bound:

async with await open_device("/dev/ttyUSB0", address=1) as ctl:
    pv1 = await ctl.read_pv(instance=1)            # loop 1
    pv2 = await ctl.read_pv(instance=2)            # loop 2 (dual-loop SKUs)
    out = await ctl.read_parameter("output_power", instance=2)

Controller.read_parameter and write_parameter validate the instance against spec.max_instance before encoding. Out-of-range instances raise WatlowValidationError.

Range validation

When the EZ-ZONE register list publishes a machine-readable range (e.g. -1999.0 to 9999.0), the registry parses it into range_min / range_max and the facade soft-validates writes:

await ctl.write_parameter("setpoint", 1e6, confirm=True)
# WatlowValidationError: value 1000000 out of range for 'setpoint' (-1999.0..9999.0)

Ranges parsed as enumeration strings ("Off (0), On (1)") are left as None and skipped by the validator. Out-of-range values still trip the device's own bound check on the wire if the host validator missed them.

Enumerations

Enumerated parameters (DataType.PACKED) decode to integer codes; the enumerations table documents the human meanings. Common ones:

Parameter Codes
heat_algorithm 62 Off, 63 PID, 71 On-Off
cool_algorithm 62 Off, 63 PID, 71 On-Off
tc_type J, K, T, S, R, B, N, E, C
display_units 15 °F, 30 °C

Reading an enumerated parameter returns the integer; the facade does not auto-decode to a label (so callers can write back round-trip safe values without an extra mapping). See registry/enumerations.py for the full table.

Reading and writing

async with await open_device("/dev/ttyUSB0", address=1) as ctl:
    # Generic read — returns ParameterEntry; the facade unwraps to Reading
    # for typed-shortcut parameters (process_value, setpoint, ...).
    entry = await ctl.read_parameter("heat_algorithm", instance=1)
    print(entry.value)                # → 63 (PID)

    # Write — confirm=True is mandatory.
    await ctl.write_parameter("heat_algorithm", 71, instance=1, confirm=True)

Controller.read_parameter returns a ParameterEntry that carries the spec, instance, decoded value, and raw bytes. Controller.read_pv and friends wrap that in a Reading for the small set of parameters where the facade guarantees a richer shape.

See also

  • ControllersController facade and capability flags.
  • CommandsREAD_PARAMETER / WRITE_PARAMETER dispatch.
  • Safety — gate order and confirm=True.
  • Registry API — full reference.
  • Design §5a — registry shape and JSON-load model.