Skip to content

sartoriuslib.registry

Unit, Sign, the parameter table (index → ParameterSpec), and the typed mode enums (FilterMode, AutoZeroMode, IsoCalMode, …) used by the typed Balance.get_X() / set_X() accessors.

Units + sign

sartoriuslib.registry.units

Unit enum + display-unit parameter mapping.

Two maps touch this file:

  1. Measurement-frame wire bytes → :class:Unit. Lives in :mod:sartoriuslib.protocol.xbpi.units (0x02 g / 0x03 kg / 0x0D mg / 0x17 N). That map decodes what a balance reports in an 8-byte measurement body.

  2. Parameter-table index 7 (display unit) → :class:Unit. Lives here as :data:DISPLAY_UNIT_CODE_TO_UNIT. That map decodes what a balance is configured to display; it is the full 24-entry table from docs/protocol.md §10.1 idx 7.

The two address spaces are different — the display-unit codes are a dense 1..24 enumeration, while the measurement-frame bytes are a sparse 6-bit ID space (0x02, 0x03, 0x0D, 0x17, …). Keeping the maps separate keeps each unit of code honest about what it actually knows.

Unknown codes decode to :attr:Unit.UNKNOWN rather than raising — forward-compatibility for firmware we have not captured.

Sign

Bases: StrEnum

Sign of a measurement as encoded on the wire.

Unit

Bases: StrEnum

Physical unit of a measurement / display unit.

Values are the short symbol (or short tag for units without a single canonical symbol) so str(Unit.G) == "g" for log lines and CSV columns.

Membership covers the 24-entry p07 display-unit table plus :attr:UNKNOWN for forward-compat on measurement-frame decoding.

BAHT class-attribute instance-attribute

BAHT = 'baht'

Thai baht weight.

CT class-attribute instance-attribute

CT = 'ct'

Metric carat (1 ct = 0.2 g).

CT_AU class-attribute instance-attribute

CT_AU = 'ct_au'

Austrian carat — non-metric; used in p07 idx 17.

DWT class-attribute instance-attribute

DWT = 'dwt'

Pennyweight (1 dwt = 24 gr).

GR class-attribute instance-attribute

GR = 'gr'

Grain (1 gr ≈ 64.79891 mg).

LB_OZ class-attribute instance-attribute

LB_OZ = 'lb_oz'

Pounds-and-ounces combined display.

MESGAL class-attribute instance-attribute

MESGAL = 'mesgal'

Mesghal (p07 idx 20).

MOMME class-attribute instance-attribute

MOMME = 'momme'

Japanese momme.

OZT class-attribute instance-attribute

OZT = 'ozt'

Troy ounce.

PARTS_PER_POUND class-attribute instance-attribute

PARTS_PER_POUND = '/lb'

Parts per pound (p07 ptplb).

T class-attribute instance-attribute

T = 't'

Metric ton.

TAEL_CN class-attribute instance-attribute

TAEL_CN = 'tl.cn'

Chinese tael.

TAEL_HK class-attribute instance-attribute

TAEL_HK = 'tl.hk'

Hong Kong tael.

TAEL_SG class-attribute instance-attribute

TAEL_SG = 'tl.sg'

Singapore / Malaysia tael.

TAEL_TW class-attribute instance-attribute

TAEL_TW = 'tl.tw'

Taiwan tael.

TOLA class-attribute instance-attribute

TOLA = 'tola'

South Asian tola.

UG class-attribute instance-attribute

UG = 'µg'

Microgram.

USERDEF class-attribute instance-attribute

USERDEF = 'userdef'

User-defined unit (p07 idx 1). A multiplier + label live in a separate register not yet located; the balance displays it scaled from grams.

unit_to_display_code

unit_to_display_code(unit)

Turn a :class:Unit into its p07 display-unit code.

Raises :class:ValueError for :attr:Unit.UNKNOWN and any :class:Unit member not in the display-unit table (should be unreachable today — every member is in the table — but stays defensive for future additions).

Source code in src/sartoriuslib/registry/units.py
def unit_to_display_code(unit: Unit) -> int:
    """Turn a :class:`Unit` into its p07 display-unit code.

    Raises :class:`ValueError` for :attr:`Unit.UNKNOWN` and any
    :class:`Unit` member not in the display-unit table (should be
    unreachable today — every member is in the table — but stays
    defensive for future additions).
    """
    try:
        return _UNIT_TO_DISPLAY_CODE[unit]
    except KeyError as exc:
        raise ValueError(
            f"{unit!r} has no display-unit code in the p07 parameter table",
        ) from exc

Parameter table

sartoriuslib.registry.parameters

Parameter-table index → typed spec map.

Drives two things at the command layer:

  1. Typed accessors on :class:~sartoriuslib.devices.balance.Balance (get_filter_mode(), set_filter_mode(...), etc.). The spec carries the enum class so the setter can validate + encode and the getter can decode to a typed value.
  2. Cache invalidation on :class:~sartoriuslib.devices.session.Session. bumps_config_counter tells the session whether a write to this index will tick 0xBA. Indices that don't (p13, p50) must still invalidate their cached entries on explicit write — the §6.3 caveat from design doc.

Only the [SURE] rows from docs/protocol.md §10.1 are modelled here. [LIKELY] rows remain reachable via raw read_parameter / write_parameter until they get promoted.

ParameterSpec dataclass

ParameterSpec(
    index,
    name,
    enum,
    writable=True,
    bumps_config_counter=True,
    families=(lambda: _ALL_FAMILIES)(),
    unit_enum=False,
)

Typed description of one well-understood parameter-table index.

Attributes:

Name Type Description
index int

0x55 TLV-21 argument value (1-based p-index).

name str

Human-readable symbol matching the get_X / set_X accessor pair on :class:~sartoriuslib.devices.balance.Balance.

enum type[IntEnum] | None

The typed :class:IntEnum the wire u8 decodes to.

writable bool

True if the balance accepts 0x56 for this index. A few indices are read-only; most [SURE] entries are writable.

bumps_config_counter bool

True if an accepted write ticks the 0xBA config counter. Used by the session cache to decide whether a write requires an explicit cache flush (False rows do — design §6.3 caveat).

families frozenset[BalanceFamily]

Families known to expose this index. Advisory only — the session's runtime availability cache is the source of truth.

unit_enum bool

Optional :class:Unit mapping for indices whose value space is a unit enumeration (just p07 today). When set, the :attr:enum field is unused and the typed getter/setter routes through :data:sartoriuslib.registry.units.DISPLAY_UNIT_CODE_TO_UNIT.

decode

decode(current)

Turn a wire u8 into the typed value (enum member or :class:Unit).

Source code in src/sartoriuslib/registry/parameters.py
def decode(self, current: int) -> IntEnum | Unit:
    """Turn a wire u8 into the typed value (enum member or :class:`Unit`)."""
    if self.unit_enum:
        from sartoriuslib.registry.units import (  # noqa: PLC0415
            DISPLAY_UNIT_CODE_TO_UNIT,
            Unit,
        )

        return DISPLAY_UNIT_CODE_TO_UNIT.get(current, Unit.UNKNOWN)
    if self.enum is None:  # pragma: no cover — every non-unit row sets enum
        raise RuntimeError(f"parameter {self.index} has no decoder")
    try:
        return self.enum(current)
    except ValueError:
        return self.enum(0)  # UNKNOWN sentinel — every mode enum sets it

encode

encode(value)

Turn a typed value back into the wire u8 for 0x56.

Raises :class:ValueError if value is not a member of the spec's enum / unit set. Accepts a plain int as an escape hatch for values outside the modelled range (but still runs through the enum constructor so mid-table gaps stay rejected).

Source code in src/sartoriuslib/registry/parameters.py
def encode(self, value: IntEnum | Unit | int) -> int:
    """Turn a typed value back into the wire u8 for ``0x56``.

    Raises :class:`ValueError` if ``value`` is not a member of the
    spec's enum / unit set. Accepts a plain ``int`` as an escape
    hatch for values outside the modelled range (but still runs
    through the enum constructor so mid-table gaps stay rejected).
    """
    if self.unit_enum:
        from sartoriuslib.registry.units import (  # noqa: PLC0415
            Unit,
            unit_to_display_code,
        )

        if isinstance(value, Unit):
            return unit_to_display_code(value)
        # IntEnum or raw int — both coerce to an int code; validate
        # against the 1..24 p07 range.
        code = int(value)
        if code not in range(1, 25):
            raise ValueError(
                f"display-unit code {code} out of range (1..24)",
            )
        return code
    if self.enum is None:  # pragma: no cover
        raise RuntimeError(f"parameter {self.index} has no encoder")
    member = value if isinstance(value, self.enum) else self.enum(int(value))
    if member.value == 0:
        raise ValueError(
            f"cannot write UNKNOWN (0) to parameter {self.index} ({self.name})",
        )
    return int(member.value)

get_parameter_spec

get_parameter_spec(index)

Look up the spec for a parameter index, or None if unmapped.

Source code in src/sartoriuslib/registry/parameters.py
def get_parameter_spec(index: int) -> ParameterSpec | None:
    """Look up the spec for a parameter index, or ``None`` if unmapped."""
    return PARAMETER_TABLE.get(index)

Mode enums

sartoriuslib.registry.modes

Typed enums for the well-understood parameter-table indices.

One :class:IntEnum per [SURE] row in docs/protocol.md §10.1. Values match the wire u8 the balance accepts / returns, so encoding is int(mode) and decoding is Mode(byte).

Each enum includes :attr:UNKNOWN (value 0) as the forward-compatibility escape hatch — a byte the library does not yet recognise decodes to UNKNOWN rather than raising, mirroring the :class:Unit / :class:Sign policy in :mod:sartoriuslib.registry.units. 0 is safe because the p* indices documented here use 1-based numbering on the wire.

AppFilter

Bases: IntEnum

p02 — application filter. Orthogonal to :class:FilterMode.

AutoZeroMode

Bases: IntEnum

p06 — auto-zero tracking on / off.

CalButtonAssignment

Bases: IntEnum

p09 — calibration-button assignment.

MSE1203S live values only; gaps reserved.

CalibrationUnit

Bases: IntEnum

p44 — calibration unit.

Note: wire encoding differs from opcode 0x79's args.

DisplayAccuracyMode

Bases: IntEnum

p08 — display-accuracy mode.

Only the four MSE1203S-live values are named. Gaps in the sparse max=18 range belong to other balances within the family and decode to :attr:UNKNOWN on devices that do not expose them.

DEFAULT class-attribute instance-attribute

DEFAULT = 1

Display at native increment.

DIV1 class-attribute instance-attribute

DIV1 = 6

div1 — increment divided by 1 (identity with DEFAULT on MSE1203S; retained as a distinct menu entry).

LOW_POWER_ON_OFF class-attribute instance-attribute

LOW_POWER_ON_OFF = 2

lponoff — power-save display toggle.

MINUS_1_DIGIT class-attribute instance-attribute

MINUS_1_DIGIT = 7

-1 digit — drops the 0x0D increment by one decimal (10x).

ExternalCalLock

Bases: IntEnum

p16 — external calibration lock.

FilterMode

Bases: IntEnum

p01 — filter / ambient mode.

Equivalent to opcode 0x26 (read_weighing_mode). Fully mapped.

IsoCalMode

Bases: IntEnum

p15 — persistent isoCAL mode.

Status-byte bit 0x10 is the attention flag, not the enable flag — see docs/protocol.md §10.1 p15.

MenuAccessMode

Bases: IntEnum

p40 — front-panel menu access.

OutputMode

Bases: IntEnum

p36 — SBI output mode (trigger × stability-filter matrix).

AUTOPRINT_STABLE class-attribute instance-attribute

AUTOPRINT_STABLE = 5

auto_w — autoprint only stable cycles.

AUTOPRINT_UNFILTERED class-attribute instance-attribute

AUTOPRINT_UNFILTERED = 4

auto_wo — autoprint each cycle regardless of stability.

MANUAL_AFTER_STABILITY class-attribute instance-attribute

MANUAL_AFTER_STABILITY = 2

ind_after — manual trigger, send after next stability.

MANUAL_AT_STABILITY class-attribute instance-attribute

MANUAL_AT_STABILITY = 3

ind_at — manual trigger, send only when already stable.

MANUAL_IMMEDIATE class-attribute instance-attribute

MANUAL_IMMEDIATE = 1

ind_no — manual trigger, send current value.

ParityMode

Bases: IntEnum

p32 / p64 — UART parity.

Shared encoding between the peripheral port (p32) and the PC-USB port (p64). Gaps 1/2 are reserved (mark/space).

StabilityDelay

Bases: IntEnum

p04 — stability delay (how long above-threshold must persist).

StabilityRange

Bases: IntEnum

p03 — stability detection band width.

StopBitsMode

Bases: IntEnum

p33 / p65 — UART stop bits.

TareBehavior

Bases: IntEnum

p05 — tare-on-stability behaviour.

TareOnPowerOn

Bases: IntEnum

p13 — tare on power-on.

Bit: this is a boot-time flag. Writing it does not bump the 0xBA config counter — see docs/protocol.md §10.1 persistence note and design doc §6.3 caveat.

ZeroRange

Bases: IntEnum

p11 — runtime auto-zero range.

decode_mode

decode_mode(enum_cls, raw)

Turn a wire u8 into a member of enum_cls.

Unrecognised codes collapse to the UNKNOWN member (value 0) so new firmware reveals stay non-crashing. Every enum in this module is guaranteed to carry an UNKNOWN = 0 member.

Source code in src/sartoriuslib/registry/modes.py
def decode_mode[E: IntEnum](enum_cls: type[E], raw: int) -> E:
    """Turn a wire u8 into a member of ``enum_cls``.

    Unrecognised codes collapse to the ``UNKNOWN`` member (value ``0``)
    so new firmware reveals stay non-crashing. Every enum in this
    module is guaranteed to carry an ``UNKNOWN = 0`` member.
    """
    try:
        return enum_cls(raw)
    except ValueError:
        return enum_cls(0)

Aliases — value normalisers

sartoriuslib.registry.aliases

Fuzzy string → typed-value resolvers.

Scripts, REPL use, and the sarto-* CLI all want to accept "Stable" / "stable" / "STABLE" / "very stable" / "very_stable" as the same :class:FilterMode. Resolvers here do one pass of normalisation (lowercase, collapse whitespace / hyphens to underscores, strip punctuation) then look the canonical form up.

Design §16 Q4 leans toward inline tables for v1; if the alias maps grow past what is readable here, promote them to a build-time artefact then.

normalise

normalise(raw)

Canonicalise raw to lower_snake_case_ish.

"Very Stable" / "very-stable" / "very.stable" all collapse to "very_stable". Unicode is lowercased but not folded — "µg" stays "µg".

Source code in src/sartoriuslib/registry/aliases.py
def normalise(raw: str) -> str:
    """Canonicalise ``raw`` to ``lower_snake_case_ish``.

    ``"Very Stable"`` / ``"very-stable"`` / ``"very.stable"`` all
    collapse to ``"very_stable"``. Unicode is lowercased but not
    folded — ``"µg"`` stays ``"µg"``.
    """
    text = raw.strip().lower()
    text = _NORMALISE_SEPARATORS.sub("_", text)
    return _NORMALISE_STRIPS.sub("", text)

resolve_auto_zero

resolve_auto_zero(raw)

Fuzzy-match to an :class:AutoZeroMode.

Source code in src/sartoriuslib/registry/aliases.py
def resolve_auto_zero(raw: str | AutoZeroMode | int) -> AutoZeroMode:
    """Fuzzy-match to an :class:`AutoZeroMode`."""
    return _resolve_enum(AutoZeroMode, _AUTO_ZERO_ALIASES, raw)

resolve_display_accuracy

resolve_display_accuracy(raw)

Fuzzy-match to a :class:DisplayAccuracyMode.

Source code in src/sartoriuslib/registry/aliases.py
def resolve_display_accuracy(raw: str | DisplayAccuracyMode | int) -> DisplayAccuracyMode:
    """Fuzzy-match to a :class:`DisplayAccuracyMode`."""
    return _resolve_enum(DisplayAccuracyMode, _DISPLAY_ACC_ALIASES, raw)

resolve_filter_mode

resolve_filter_mode(raw)

Fuzzy-match to a :class:FilterMode. Raises on unknown input.

Source code in src/sartoriuslib/registry/aliases.py
def resolve_filter_mode(raw: str | FilterMode | int) -> FilterMode:
    """Fuzzy-match to a :class:`FilterMode`. Raises on unknown input."""
    return _resolve_enum(FilterMode, _FILTER_ALIASES, raw)

resolve_isocal_mode

resolve_isocal_mode(raw)

Fuzzy-match to an :class:IsoCalMode.

Source code in src/sartoriuslib/registry/aliases.py
def resolve_isocal_mode(raw: str | IsoCalMode | int) -> IsoCalMode:
    """Fuzzy-match to an :class:`IsoCalMode`."""
    return _resolve_enum(IsoCalMode, _ISOCAL_ALIASES, raw)

resolve_menu_access

resolve_menu_access(raw)

Fuzzy-match to a :class:MenuAccessMode.

Source code in src/sartoriuslib/registry/aliases.py
def resolve_menu_access(raw: str | MenuAccessMode | int) -> MenuAccessMode:
    """Fuzzy-match to a :class:`MenuAccessMode`."""
    return _resolve_enum(MenuAccessMode, _MENU_ACCESS_ALIASES, raw)

resolve_output_mode

resolve_output_mode(raw)

Fuzzy-match to an :class:OutputMode.

Source code in src/sartoriuslib/registry/aliases.py
def resolve_output_mode(raw: str | OutputMode | int) -> OutputMode:
    """Fuzzy-match to an :class:`OutputMode`."""
    return _resolve_enum(OutputMode, _OUTPUT_ALIASES, raw)

resolve_tare_behavior

resolve_tare_behavior(raw)

Fuzzy-match to a :class:TareBehavior.

Source code in src/sartoriuslib/registry/aliases.py
def resolve_tare_behavior(raw: str | TareBehavior | int) -> TareBehavior:
    """Fuzzy-match to a :class:`TareBehavior`."""
    return _resolve_enum(TareBehavior, _TARE_ALIASES, raw)

resolve_unit

resolve_unit(raw)

Fuzzy-match raw to a :class:Unit member.

Raises :class:UnknownUnitError if no alias matches.

Source code in src/sartoriuslib/registry/aliases.py
def resolve_unit(raw: str | Unit) -> Unit:
    """Fuzzy-match ``raw`` to a :class:`Unit` member.

    Raises :class:`UnknownUnitError` if no alias matches.
    """
    if isinstance(raw, Unit):
        return raw
    key = normalise(raw)
    if key in _UNIT_ALIASES:
        return _UNIT_ALIASES[key]
    raise UnknownUnitError(
        f"unknown unit {raw!r}",
        context=ErrorContext(extra={"input": raw, "normalised": key}),
    )