mirror of
https://github.com/AletheiaVox/signal_bridge_remote.git
synced 2026-10-07 03:18:17 +08:00
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:
@@ -190,13 +190,22 @@ _OUTPUT_PARAMS = {
|
||||
"description": "Duration in seconds. 0 = stay on until stop command.",
|
||||
"default": 0,
|
||||
},
|
||||
"feature_index": {
|
||||
"type": "integer",
|
||||
"description": (
|
||||
"Target a specific actuator by index when a device has multiple "
|
||||
"actuators of the same type (e.g. Dolce motor 0 = primary, "
|
||||
"motor 1 = secondary). Omit to drive all matching actuators together."
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _make_output_handler(output_type: OutputType):
|
||||
"""Factory for output command handlers."""
|
||||
async def handler(
|
||||
device: str = "all", intensity: float = 0.5, duration: float = 0, **kw
|
||||
device: str = "all", intensity: float = 0.5, duration: float = 0,
|
||||
feature_index: Optional[int] = None, **kw
|
||||
) -> str:
|
||||
clamped = max(0.0, min(1.0, float(intensity)))
|
||||
cmd = DeviceCommand(
|
||||
@@ -204,6 +213,7 @@ def _make_output_handler(output_type: OutputType):
|
||||
device=device,
|
||||
intensity=clamped,
|
||||
duration=max(0.0, float(duration)),
|
||||
feature_index=feature_index,
|
||||
)
|
||||
return await _send(cmd.model_dump(), intensity=clamped)
|
||||
return handler
|
||||
@@ -311,6 +321,13 @@ _PATTERN_PARAMS = {
|
||||
"description": "Duration in seconds",
|
||||
"default": 10,
|
||||
},
|
||||
"feature_index": {
|
||||
"type": "integer",
|
||||
"description": (
|
||||
"Target a specific actuator by index when a device has multiple "
|
||||
"actuators of the same type. Omit to drive all matching actuators."
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -321,6 +338,7 @@ def _make_pattern_handler(pattern_name: str):
|
||||
intensity: float = 0.6,
|
||||
duration: float = 10,
|
||||
hold_seconds: float = 0,
|
||||
feature_index: Optional[int] = None,
|
||||
**kw,
|
||||
) -> str:
|
||||
clamped = max(0.0, min(1.0, float(intensity)))
|
||||
@@ -331,6 +349,7 @@ def _make_pattern_handler(pattern_name: str):
|
||||
intensity=clamped,
|
||||
duration=max(0.0, float(duration)),
|
||||
hold_seconds=max(0.0, float(hold_seconds)),
|
||||
feature_index=feature_index,
|
||||
)
|
||||
return await _send(cmd.model_dump(), intensity=clamped)
|
||||
return handler
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user