sartoriuslib.commands¶
Declarative command specs and the per-protocol variant ABCs. See Commands for the catalogue and Design §4.2 for the layer's architecture.
Public surface¶
sartoriuslib.commands ¶
Semantic command specs with per-protocol variants.
See design doc §4.2 (shape) and §6 (facade surface).
Command
dataclass
¶
Command(
name,
xbpi=None,
sbi=None,
family_hints=_NO_FAMILIES,
capability_hints=NO_CAPABILITY,
safety=SafetyTier.READ_ONLY,
min_firmware=None,
max_firmware=None,
parameterized=False,
)
Declarative command spec.
Protocol mismatch (active protocol's variant is None) is the
only hard pre-I/O gate that comes from this spec. family_hints
and capability_hints are advisory priors consulted by the session
— they upgrade to hard refusals only under strict=True (design
doc §6.1).
parameterized flags commands whose request carries an
application-level argument that selects a sub-resource (a sensor
index, a parameter-table row, an area number, ...). Some firmwares
answer xBPI 0x04 ("unsupported/unknown opcode") for an
out-of-range argument value when they should answer 0x10 (index
out of range) — the wire is technically incorrect but the device is
what we have. The session translates 0x04 on a parameterized
command into :class:SartoriusIndexOutOfRangeError (the semantic
intent) and skips the
:attr:Availability.UNSUPPORTED-sticky cache update so an
out-of-range index for sensor 4 doesn't lock out sensors 0-3 for
the rest of the session. Discovered on hardware day when probing
temperature(4) poisoned temperature(0..3).
CommandContext
dataclass
¶
Context threaded through command encode/decode.
Variants stay pure functions of (ctx, request) -> bytes /
(reply, ctx) -> Resp by receiving the small amount of session
state they need through this struct: the active protocol, SBN
addressing, and (when known) firmware and family.
SbiVariant ¶
Bases: ABC
SBI variant of a command.
Declared alongside :class:XbpiVariant so :class:Command can carry
both variant slots and the session can refuse pre-I/O when the active
protocol has no variant.
expect_lines is the number of newline-terminated reply lines the
variant expects. Control tokens like ESC T / ESC V set this
to 0 because the device acknowledges silently.
decode
abstractmethod
¶
XbpiVariant ¶
Bases: ABC
xBPI variant of a command.
Subclasses should set opcode as a class attribute and override
:meth:encode and :meth:decode. Keep subclasses frozen
(@dataclass(frozen=True, slots=True)) to preserve the
"specs are immutable" invariant at the command layer.
Base — Command[Req, Resp], variants, context¶
sartoriuslib.commands.base ¶
Command[Req, Resp] + XbpiVariant + SbiVariant + CommandContext.
One :class:Command carries at most one variant per protocol; either or
both may be None. The session picks the variant matching its active
protocol and dispatches through variant.encode / variant.decode.
Per design doc §4.2, per-protocol work lives on variant objects, not
as methods bolted onto :class:Command. That keeps the opcode or SBI
token co-located with the logic that uses it, makes "not implemented
for this protocol" trivially expressible as None, and keeps the
command dataclass pure metadata.
Command
dataclass
¶
Command(
name,
xbpi=None,
sbi=None,
family_hints=_NO_FAMILIES,
capability_hints=NO_CAPABILITY,
safety=SafetyTier.READ_ONLY,
min_firmware=None,
max_firmware=None,
parameterized=False,
)
Declarative command spec.
Protocol mismatch (active protocol's variant is None) is the
only hard pre-I/O gate that comes from this spec. family_hints
and capability_hints are advisory priors consulted by the session
— they upgrade to hard refusals only under strict=True (design
doc §6.1).
parameterized flags commands whose request carries an
application-level argument that selects a sub-resource (a sensor
index, a parameter-table row, an area number, ...). Some firmwares
answer xBPI 0x04 ("unsupported/unknown opcode") for an
out-of-range argument value when they should answer 0x10 (index
out of range) — the wire is technically incorrect but the device is
what we have. The session translates 0x04 on a parameterized
command into :class:SartoriusIndexOutOfRangeError (the semantic
intent) and skips the
:attr:Availability.UNSUPPORTED-sticky cache update so an
out-of-range index for sensor 4 doesn't lock out sensors 0-3 for
the rest of the session. Discovered on hardware day when probing
temperature(4) poisoned temperature(0..3).
CommandContext
dataclass
¶
Context threaded through command encode/decode.
Variants stay pure functions of (ctx, request) -> bytes /
(reply, ctx) -> Resp by receiving the small amount of session
state they need through this struct: the active protocol, SBN
addressing, and (when known) firmware and family.
SbiVariant ¶
Bases: ABC
SBI variant of a command.
Declared alongside :class:XbpiVariant so :class:Command can carry
both variant slots and the session can refuse pre-I/O when the active
protocol has no variant.
expect_lines is the number of newline-terminated reply lines the
variant expects. Control tokens like ESC T / ESC V set this
to 0 because the device acknowledges silently.
decode
abstractmethod
¶
XbpiVariant ¶
Bases: ABC
xBPI variant of a command.
Subclasses should set opcode as a class attribute and override
:meth:encode and :meth:decode. Keep subclasses frozen
(@dataclass(frozen=True, slots=True)) to preserve the
"specs are immutable" invariant at the command layer.
Common request / response types¶
sartoriuslib.commands.common ¶
Request/response dataclasses shared across command groups (stub).
Identity¶
sartoriuslib.commands.identity ¶
Identity-read primitives composed by :meth:Balance.identify.
Each primitive decodes one opcode's reply into a typed value; the
balance composes them into a :class:DeviceInfo.
xBPI opcodes per docs/protocol.md §7.1, §7.10:
0x00read_software_version → subtype0x4A, 10-byte packed blob0x01read_factory_number → subtype0x45, 5-byte blob0x02read_weigh_cell_model → subtype0x54, 20-byte ASCII (null-padded)0x05read_oem_text → subtype0x50, 16-byte ASCII0x07read_manufacturer → subtype0x50, 16-byte ASCII0x71read_sbn_address → subtype0x21, 1 byte
Weight¶
sartoriuslib.commands.weight ¶
Weight-read commands — net, gross, stored tare (std + hires variants).
xBPI opcodes per docs/protocol.md §7.4. All decode to the
protocol-neutral :class:Reading dataclass via the shared
:func:_measurement_reply_to_reading helper so the SBI variants
produce semantically equivalent output.
hires variants (0x1F, 0x21) require a TLV-21 arg (1 =
10×, 2 = 100×). Standard-resolution reads (0x1E, 0x20,
0x22) take no args.
Tare / zero¶
sartoriuslib.commands.tare ¶
Tare and zero commands — TARE (0x14) and ZERO (0x18).
Both return an xBPI ACK (subtype 0x00, empty body). Both are
:attr:SafetyTier.STATEFUL — transient state change, no EEPROM write,
runs freely without confirm=True (design §6.1). See
docs/protocol.md §7.3.
TareRequest
dataclass
¶
No-arg request; tare/zero take no parameters at the xBPI layer.
Status¶
sartoriuslib.commands.status ¶
Status command — xBPI 0x30 (full 8-byte status block).
Decodes to the protocol-neutral :class:BalanceStatus dataclass.
Derives :class:BalanceState from the state byte per
docs/protocol.md §8.2: 0x82 overload, 0x84 underload, and
bit 0x08 marks stable on both Cubis and non-Cubis families (just in
different bytes — the parser already normalises).
Metrology¶
sartoriuslib.commands.metrology ¶
Metrology commands — capacity, increment, temperature.
xBPI opcodes per docs/protocol.md §7.2 and §9:
0x0Cread_max → typed_float (Quantity), TLV-21 area arg0x0Dread_increment → typed_float (Quantity), TLV-21 area arg0x76read_temperature → typed_float (TemperatureReading), TLV-21 sensor arg
Capacity and increment are reported in the balance's current display
unit; the opcode reply does not carry a self-describing unit byte
(contrast the 8-byte measurement body's byte [6]). The typed results
here therefore use :attr:Unit.UNKNOWN — callers who need a concrete
unit read the display-unit parameter (p07) separately, or inspect
Reading.unit from :meth:Balance.poll. This is more honest than
guessing grams.
Temperature replies use the sentinel 7f ff ff ff for "sensor not
installed" (docs/protocol.md §9). The :class:TemperatureReading
decode maps that to celsius=None instead of NaN.
MetrologyRequest
dataclass
¶
Capacity / increment request.
area selects the weighing range on multi-range balances;
single-range units (all currently captured) report the same value
regardless. Defaults to 0 because that is the only area every
balance is known to accept.
TemperatureRequest
dataclass
¶
Per-sensor temperature request (TLV-21 sensor index).
Parameters¶
sartoriuslib.commands.parameters ¶
Parameter-table raw access — 0x55 read, 0x56 write.
Per docs/protocol.md §7.8 and §5.2 / §5.3:
0x55read_parameter: TX[0x55][TLV-21 idx], RX body contains two u8 TLVs(current, max). The subtype byte0x21doubles as the first TLV's tag, so decode prepends the subtype before parsing.0x56write_parameter: TX[0x56][TLV-21 idx][TLV-21 val], RX is an xBPI ACK.
Writing is :attr:SafetyTier.PERSISTENT — the session refuses the
call without confirm=True (design §6.1).
Typed accessors (Balance.get_filter_mode() / set_filter_mode())
build on these two primitives plus the
:class:sartoriuslib.registry.parameters.ParameterSpec table.
Calibration¶
sartoriuslib.commands.calibration ¶
Calibration commands — last cal record (0xB9) and internal adjust (0x28).
Per docs/protocol.md §7.7 and §7.12:
0xB9read_last_cal_record: subtype0x51, 17-byte body. Layout in §7.12. Ignores TLV args.READ_ONLY.0x28start_adjustment: TLV-21 arg selects cal type (e.g.0x78/120= internal adjust). Side-effecting, observable physically —DANGEROUS.
The CalRecord dataclass preserves the three-tier storage
distinction from §7.12: :attr:CalRecord.temperature_celsius can be
present while :attr:signature + :attr:counters are all-zero (the
RAM metadata buffer was cleared by a cold boot but the temperature
field lives in a separate backing location).
System¶
sartoriuslib.commands.system ¶
System commands — config counter, save/reload menu.
xBPI opcodes per docs/protocol.md §7.8 and §7.11:
0xBAconfig_generation_counter: u8 register that increments on most runtime-config changes. The cache-invalidation signal that powers :class:Session's result cache.READ_ONLY.0x47save_menu_to_eeprom: persist current menu to EEPROM.PERSISTENT— requiresconfirm=True.0x46read_menu_from_eeprom: reload saved menu from EEPROM.PERSISTENT— requiresconfirm=True.
The 0xBA caveat from docs/protocol.md §10.1 and design doc
§6.3: not every persistent write ticks the counter (p13 / p50
are known exceptions). The cache treats those writes as
cache-invalidating regardless — see :class:ParameterSpec and the
session's invalidation hooks.
Raw¶
sartoriuslib.commands.raw ¶
Raw protocol escape hatches for RE and advanced users.
Does not use the :class:Command spec machinery: the opcode is a
per-call parameter, not a compile-time constant. Instead, the caller
hands bytes to :meth:Balance.raw_xbpi, which routes through
:meth:Session.execute_raw_xbpi. That method applies a single hard
safety gate — the opcode must be on :data:SAFE_READ_ONLY_OPCODES or
the caller must pass confirm=True. See design §6.1.
The SBI safe-list lives in :mod:sartoriuslib.protocol.sbi.tables because
SBI tokens are also used by the line codec and raw CLI.