feat: feature_index — target individual motors on multi-actuator devices

Ported from the Android edition and completed for the Python relays
(the Android repo only implemented the phone side in Kotlin):

- models.py / mcp_tools.py: optional feature_index on every output and
  pattern tool, passed through to the phone relay.
- relay_client.py: routes targeted writes through buttplug-py's
  per-feature API (device.features[i].run_output) and validates the
  index up front so a bad one fails the ack with the valid indices
  listed, instead of dying silently inside a pattern task.
- termux_relay_v3.py: ScalarCmd entries filtered to the requested
  actuator index, same fallback semantics as the Android relay engine.

Lets Claude drive e.g. a Dolce's internal and external motors
independently (feature_index 0 / 1) instead of always both together.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Aletheia
2026-07-07 20:11:01 +02:00
parent 263e6e9b75
commit 136920b717
4 changed files with 101 additions and 31 deletions

View File

@@ -45,6 +45,7 @@ class DeviceCommand(BaseModel):
device: str = "all"
intensity: float = Field(0.5, ge=0.0, le=1.0)
duration: float = Field(0.0, ge=0.0) # 0 = indefinite
feature_index: Optional[int] = None # target a specific actuator by index
class PatternCommand(BaseModel):
@@ -56,6 +57,7 @@ class PatternCommand(BaseModel):
intensity: float = Field(0.6, ge=0.0, le=1.0)
duration: float = Field(10.0, ge=0.0)
hold_seconds: float = Field(0.0, ge=0.0) # escalate only: 0 = hold at peak indefinitely
feature_index: Optional[int] = None # target a specific actuator by index
class StopCommand(BaseModel):