pyisyox.schema.editor module¶
Editor dataclasses and bidirectional codec for IoX profile editors.
An editor (e.g. "I_OL", "I_TSTAT_MODE") is referenced by both
nodedef properties and command parameters and defines a bidirectional
contract — read-side decode and write-side validation/encode. Write-side
constraints beyond min/max/prec are the subset mask
("0-3,5-7" excludes 4) and the names enum option list.
Send-side scaling¶
The controller does all device-side scaling itself, keyed off the
UOM appended to the /cmd URL — proven on hardware:
/cmd/DON/100/100 → 39 % (100 read as a UOM-100 0-255 byte) vs
/cmd/DON/100/51 → 100 % (100 read as UOM-51 percent). So the codec
validates input (enum-name resolution, min/max, subset)
but sends the displayed value verbatim with its range UOM; it does
not rewrite the number (no *10**prec rescale, no half-degree doubling).
The eisy web UI does the same (/cmd/setTemp/10.4/17). decode
keeps its precision-aware formatting for display helpers, but the
property read path normalises by the wire UOM and never calls it.
- class EditorRange(uom, min=None, max=None, precision=0, subset=<factory>, names=<factory>, step=None, nls_prefix=None)[source]¶
Bases:
objectOne range entry within an editor.
An editor may carry multiple ranges (e.g., a temperature editor with Fahrenheit and Celsius variants), each tied to a distinct UOM.
- Variables:
uom (str) – Unit-of-measure identifier (string, indexes into the IoX UOM table).
min (float | None) – Lower numeric bound for raw values (inclusive).
Nonewhen the range is purely enumerative (subset only).max (float | None) – Upper numeric bound (inclusive).
precision (int) – Decimal precision applied to raw values (e.g., raw
6839withprecision=4displays as0.6839). The wire keys it as"prec"; Python attribute spells it out.subset (set[int]) – Resolved set of valid raw integers, narrower than
[min, max]. Empty when the full[min, max]range is valid.names (dict[int, str]) – Mapping of raw integer → display name for enumerated values (e.g.,
{0: "Off", 1: "Heat", 2: "Cool"}).step (float | None) – Increment hint for numeric (slider-shaped) ranges, in displayed units — e.g.
0.5on a half-degree setpoint editor.Nonewhen the editor doesn’t specify one (callers then derive a step fromprecision). Not used by the codec; it’s a UI hint, surfaced for consumers that build number entities.nls_prefix (str | None) – The NLS string-table prefix this range’s enum option names live under (the
_N_<nls>segment of an encoded editor id, or a named editor’s index nls).namesstays empty until something resolves it against an NLS table (the controller does it inline for/rest/profilesfamilies;Profile.find_editor()does it for encoded editors when the profile has an NLS table loaded).
- Parameters:
- classmethod from_json(raw)[source]¶
Build a range from a JSON object.
- Parameters:
raw (dict)
- Return type:
- is_valid(raw_value)[source]¶
True if
raw_valueis acceptable for outbound commands.Used for subset validation only (prec=0, enum-shaped editors). Numeric editors with
prec>0validate the displayed value and send it as-is (no scaling — the controller scales device-side from the UOM; seeEditor.encode()).min/maxin the IoX schema are stored in displayed form (e.g.min=5.0on a UOM-4 °C setpoint editor withprec=1means 5.0 °C, not raw 5).
- exception EditorCodecError[source]¶
Bases:
ValueErrorRaised when an editor codec cannot encode or decode a value.
- class Editor(id, ranges=<factory>)[source]¶
Bases:
objectA profile editor — bidirectional codec for property and parameter values.
Encoding direction (
encode): user input (int or enum name) → raw int suitable to send to the controller, with subset/range validation.Decoding direction (
decode): raw int from the controller → display string (enum name if known, else formatted number with prec/uom).For multi-range editors the codec selects the range whose UOM matches a caller-supplied
uomhint, falling back to the first range. Most editors carry a single range.- Parameters:
id (str)
ranges (list[EditorRange])
- ranges: list[EditorRange]¶
- classmethod from_encoded_id(editor_id)[source]¶
Decode a self-describing encoded editor id into an
Editor.IoX lets a simple editor be referenced by an id that fully encodes its (single) range instead of pointing at a named
<editor>element — handy for the dynamically-generated Z-Wave nodedefs where most editors are spelled inline. The grammar (see https://developer.isy.io/docs/API/IoX/editors#encoded-editor-id):_<uom>_<prec>— implied bounds[-2147483647, 2147483647]optionally one of
_R_<min>_<max>(numeric range; a leadingmmakes a bound negative —_17_2_R_m5_10=> -5..10) or_S_<lowMask>[_<highMask>](subset as a 32/64-bit hex bitmask —_17_1_S_FF00FF00⇒{8..15, 24..31})optionally a trailing
_N_<nls>NLS-prefix segment
Returns
Noneifeditor_iddoesn’t parse as an encoding (so callers can fall back to a table lookup). The_N_<nls>segment is captured asEditorRange.nls_prefixbut not resolved here —Profile.find_editor()fillsnamesfrom it when the profile carries an NLS table. Range / subset validation works regardless.
- range_for(uom=None)[source]¶
Pick the range matching
uom, or the first range if no hint.- Parameters:
uom (str | None)
- Return type:
- decode(raw_value, uom=None)[source]¶
Decode a raw value to its display string.
Enum lookup first (when
namescovers the value), otherwise a precision-aware numeric string. Does not append the unit — callers format the unit separately based on the range’suom.When
uomisn’t given and the editor has multiple ranges, the enum-name lookup scans every range (so e.g. an editor whose first range is a 0-100 % scale and whose second is a tiny{1: "Previous Value"}index still decodes1to its name).UOM-101 / “degrees” with
prec=0halves the raw value (Insteon half-degree convention).
- encode(value, uom=None)[source]¶
Validate user input and return the value to put on the wire.
Two paths within a range:
Enum name (str matching ``names``) — returns the matching raw int verbatim.
min/maxdon’t apply.Numeric (int/float, or string parsed as float) — the displayed value. Validated against
[min, max]and thesubsetmask (both stored in displayed form), then returned as-is (int when integral, else float). The controller does device-side scaling from the appended UOM — the codec does not rewrite the number (no*10**precrescale, no half-degree doubling).
When
uomis given, only that range is tried. Otherwise every range is tried in order and the first that acceptsvaluewins — multi-range editors likeZW_DIM_PERCENT(range 0 is a tiny{1: "Previous Value"}index, range 1 is the 0-100 % scale) need this so a plain75lands in the percent range instead of being rejected by the index range.Raises
EditorCodecErrorif no range acceptsvalue.
- encode_param(value, uom=None)[source]¶
Like
encode(), but also returns the UOM of the range used.Command-send code appends each parameter as
/{value}/{uom}and the controller scales device-side from that UOM (proven:/cmd/DON/100/100→ 39 %,/cmd/DON/100/51→ 100 %), so the UOM has to be the one belonging to the range that actually accepted the value, not alwaysranges[0]— for a multi-range editor likeZW_DIM_PERCENTa plain75is encoded by the 0-100 % range (uom51), so/cmd/DON/75/51goes on the wire, not/cmd/DON/75/25.