Skip to content

Commands

Commands are accepted only after hello, query_state, and subscribe. command_id is retired and rejected.

Single-Motor Commands

Supported command.op values:

  • move
  • open
  • close
  • stop
  • cycle_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:

  • move
  • stop

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.