Commands¶
Commands are accepted only after hello, query_state, and subscribe. command_id is retired and rejected.
Single-Motor Commands¶
Supported command.op values:
moveopenclosestopcycle_motion
move requires target_percent, a JSON number from 0 through 100 with up to two decimal places. External clients cannot send target_encoder.
cycle_motion advances the motor-managed open/stop/close cycle. It is single-motor only, carries no target fields, and requires calibrated limits.
Setup movement, limit configuration, direction, speed, commissioning, and factory-reset operations are internal-only.
Group Commands¶
Supported group_command.op values:
movestop
Group move is percent-only. Use group_command for stored-group movements
because the processor applies synchronized fan-out timing internally.
Use a stored group when all of its members share one command intent. External
group commands support only move and stop; group topology remains hidden and
must not be expanded by the driver.
If at least one group member accepts, the group response is accepted. Partial member failures do not add per-member fields in Phase 1. If all members fail, the response is rejected.
Stateless Multi-Motor Moves¶
Use multi_motor_move when a scene assigns a different percentage to each
motor, such as aligning hembars across different drops:
{"type":"multi_motor_move","targets":[{"motor_id":"5b8ffb9d-dfce-7cf4-8a72-4349274a9011","target_percent":67.45},{"motor_id":"5b8ffb9e-0c71-7f00-9dbf-032216914507","target_percent":71.2}]}
The ordered targets array must be non-empty and each motor_id must be unique.
Targets are motor-only and percent-only; the message carries no op, does not
accept groups, and does not persist a scene. The processor selects one
synchronized dispatch time from the slowest eligible target transport.
The terminal command_response.results array preserves target order and reports
each motor as accepted or rejected. A rejected result includes error_code
and retryable. The top-level status is accepted when at least one target is
accepted; it is rejected when all targets fail.
Separate single-motor commands remain valid, but they are not synchronized and
each consumes the shared command rate budget. One multi_motor_move consumes
one logical request.
Optional Retry IDs¶
Commands without request_id execute without deduplication. ID-less clients must not blindly retry after a disconnect because the physical command may have executed even if its response was lost.
When supplied on command, group_command, or multi_motor_move, request_id is a 60-second retry key scoped to the authenticated client:
- an exact completed retry replays the original response without re-execution
- an exact retry while the original is in flight returns
conflict,retryable=true - a changed payload under the same ID returns
conflict,retryable=false - reuse after 60 seconds is treated as a new command
The retry fingerprint excludes the correlation field request_id. For
multi_motor_move, it includes the complete ordered targets array, so
reordering targets changes the fingerprint. It also treats explicit JSON
null like omission only for runtime-optional fields whose parsers make those
forms equivalent. All other fields, including unknown additive fields,
participate in the fingerprint. External clients should omit unused or
unsupported fields and retransmit the same command semantics on an exact retry.
External commands never carry protocol_version. The internal-only command
fields target_encoder, direction, pulses, count, and speed_rpm are
rejected even on move requests.