haply 1.3.0

Haply Robotics Client Library for the Inverse Service
Documentation
//! Device configuration, filters, grip pairing, and utility HTTP endpoints.
//! All methods are `impl InverseHttpClient` — Rust allows multiple impl blocks across files.

use crate::device_model::{
    CoordinateSystem, DampingConfig, ForceGateConfig, GravityCompensation, Handedness, HomeReturn,
    MountConfig, NavigationConfigure, NavigationFullResponse, PresetConfig, SetTransformCmd,
    TorqueScaling,
};
use reqwest::Method;

use super::client::InverseHttpClient;
use super::types::PairingResponse;

impl InverseHttpClient {
    // ============================================================
    // Device configuration — non-session-scoped (Inverse3 only)
    // ============================================================

    pub async fn get_handedness(&self, device_type: &str, id: &str) -> Result<Handedness, Box<dyn std::error::Error + Send + Sync>> {
        self.get_device_config(device_type, id, "handedness", None).await
    }

    pub async fn set_handedness(&self, device_type: &str, id: &str, value: &Handedness) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.set_device_config(Method::POST, device_type, id, "handedness", None, value).await
    }

    pub async fn delete_handedness(&self, device_type: &str, id: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.delete_device_config(device_type, id, "handedness", None).await
    }

    pub async fn get_torque_scaling(&self, device_type: &str, id: &str) -> Result<TorqueScaling, Box<dyn std::error::Error + Send + Sync>> {
        self.get_device_config(device_type, id, "torque_scaling", None).await
    }

    pub async fn set_torque_scaling(&self, device_type: &str, id: &str, value: &TorqueScaling) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.set_device_config(Method::POST, device_type, id, "torque_scaling", None, value).await
    }

    pub async fn delete_torque_scaling(&self, device_type: &str, id: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.delete_device_config(device_type, id, "torque_scaling", None).await
    }

    pub async fn get_gravity_compensation(&self, device_type: &str, id: &str) -> Result<GravityCompensation, Box<dyn std::error::Error + Send + Sync>> {
        self.get_device_config(device_type, id, "gravity_compensation", None).await
    }

    pub async fn set_gravity_compensation(&self, device_type: &str, id: &str, value: &GravityCompensation) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.set_device_config(Method::POST, device_type, id, "gravity_compensation", None, value).await
    }

    pub async fn delete_gravity_compensation(&self, device_type: &str, id: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.delete_device_config(device_type, id, "gravity_compensation", None).await
    }

    pub async fn get_home_return(&self, device_type: &str, id: &str) -> Result<HomeReturn, Box<dyn std::error::Error + Send + Sync>> {
        self.get_device_config(device_type, id, "home_return", None).await
    }

    pub async fn set_home_return(&self, device_type: &str, id: &str, value: &HomeReturn) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.set_device_config(Method::POST, device_type, id, "home_return", None, value).await
    }

    pub async fn delete_home_return(&self, device_type: &str, id: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.delete_device_config(device_type, id, "home_return", None).await
    }

    // ============================================================
    // Device configuration — session-scoped (all devices)
    // ============================================================

    pub async fn get_basis(&self, device_type: &str, id: &str, session: Option<&str>) -> Result<CoordinateSystem, Box<dyn std::error::Error + Send + Sync>> {
        self.get_device_config(device_type, id, "basis", session).await
    }

    pub async fn set_basis(&self, device_type: &str, id: &str, session: Option<&str>, value: &CoordinateSystem) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.set_device_config(Method::POST, device_type, id, "basis", session, value).await
    }

    pub async fn delete_basis(&self, device_type: &str, id: &str, session: Option<&str>) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.delete_device_config(device_type, id, "basis", session).await
    }

    pub async fn get_mount(&self, device_type: &str, id: &str, session: Option<&str>) -> Result<MountConfig, Box<dyn std::error::Error + Send + Sync>> {
        self.get_device_config(device_type, id, "mount", session).await
    }

    pub async fn set_mount(&self, device_type: &str, id: &str, session: Option<&str>, value: &MountConfig) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.set_device_config(Method::POST, device_type, id, "mount", session, value).await
    }

    pub async fn patch_mount(&self, device_type: &str, id: &str, session: Option<&str>, value: &MountConfig) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.set_device_config(Method::PATCH, device_type, id, "mount", session, value).await
    }

    pub async fn delete_mount(&self, device_type: &str, id: &str, session: Option<&str>) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.delete_device_config(device_type, id, "mount", session).await
    }

    pub async fn get_preset(&self, device_type: &str, id: &str, session: Option<&str>) -> Result<PresetConfig, Box<dyn std::error::Error + Send + Sync>> {
        self.get_device_config(device_type, id, "preset", session).await
    }

    pub async fn set_preset(&self, device_type: &str, id: &str, session: Option<&str>, value: &PresetConfig) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.set_device_config(Method::POST, device_type, id, "preset", session, value).await
    }

    pub async fn delete_preset(&self, device_type: &str, id: &str, session: Option<&str>) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.delete_device_config(device_type, id, "preset", session).await
    }

    pub async fn get_state_transform(&self, device_type: &str, id: &str, session: Option<&str>) -> Result<SetTransformCmd, Box<dyn std::error::Error + Send + Sync>> {
        let mut url = self.device_url(device_type, id, "state/transform");
        Self::append_session(&mut url, session);
        self.request_envelope(self.client.get(&url)).await
    }

    pub async fn set_state_transform(&self, device_type: &str, id: &str, session: Option<&str>, value: &SetTransformCmd) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        let mut url = self.device_url(device_type, id, "state/transform");
        Self::append_session(&mut url, session);
        self.request_envelope_void(self.client.post(&url).json(value)).await
    }

    pub async fn patch_state_transform(&self, device_type: &str, id: &str, session: Option<&str>, value: &SetTransformCmd) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        let mut url = self.device_url(device_type, id, "state/transform");
        Self::append_session(&mut url, session);
        self.request_envelope_void(self.client.patch(&url).json(value)).await
    }

    pub async fn delete_state_transform(&self, device_type: &str, id: &str, session: Option<&str>) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        let mut url = self.device_url(device_type, id, "state/transform");
        Self::append_session(&mut url, session);
        self.request_envelope_void(self.client.delete(&url)).await
    }

    // ============================================================
    // Device filters — session-scoped (Inverse3 only)
    // ============================================================

    pub async fn get_filters_damping(&self, device_type: &str, id: &str, session: Option<&str>) -> Result<DampingConfig, Box<dyn std::error::Error + Send + Sync>> {
        self.get_device_config(device_type, id, "filters/damping", session).await
    }

    pub async fn set_filters_damping(&self, device_type: &str, id: &str, session: Option<&str>, value: &DampingConfig) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.set_device_config(Method::POST, device_type, id, "filters/damping", session, value).await
    }

    pub async fn delete_filters_damping(&self, device_type: &str, id: &str, session: Option<&str>) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.delete_device_config(device_type, id, "filters/damping", session).await
    }

    pub async fn get_filters_force_gate(&self, device_type: &str, id: &str, session: Option<&str>) -> Result<ForceGateConfig, Box<dyn std::error::Error + Send + Sync>> {
        self.get_device_config(device_type, id, "filters/force_gate", session).await
    }

    pub async fn set_filters_force_gate(&self, device_type: &str, id: &str, session: Option<&str>, value: &ForceGateConfig) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.set_device_config(Method::POST, device_type, id, "filters/force_gate", session, value).await
    }

    pub async fn delete_filters_force_gate(&self, device_type: &str, id: &str, session: Option<&str>) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.delete_device_config(device_type, id, "filters/force_gate", session).await
    }

    // ============================================================
    // Grip pairing
    // ============================================================

    pub async fn get_paired_with(&self, device_type: &str, id: &str) -> Result<PairingResponse, Box<dyn std::error::Error + Send + Sync>> {
        self.get_device_config(device_type, id, "paired_with", None).await
    }

    pub async fn set_paired_with(&self, device_type: &str, id: &str, body: &serde_json::Value) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.set_device_config(Method::POST, device_type, id, "paired_with", None, body).await
    }

    pub async fn delete_paired_with(&self, device_type: &str, id: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        self.delete_device_config(device_type, id, "paired_with", None).await
    }

    // ============================================================
    // Utility endpoints
    // ============================================================

    pub async fn reset_port(&self, port: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        let url = format!("{}/ports/{}/reset", self.base_url, port);
        self.request_envelope_void(self.client.post(&url)).await
    }

    pub async fn save_configuration(&self, device_id: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        let url = format!("{}/save_configuration", self.base_url);
        let body = serde_json::json!({ "device_id": device_id });
        self.request_envelope_void(self.client.post(&url).json(&body)).await
    }

    // ============================================================
    // Navigation
    // ============================================================

    /// Get navigation settings, state, and status. `GET /{type}/{id}/config/navigation?session=<expr>`
    pub async fn get_navigation(&self, device_type: &str, id: &str, session: Option<&str>) -> Result<NavigationFullResponse, Box<dyn std::error::Error + Send + Sync>> {
        let mut url = self.config_url(device_type, id, "navigation");
        Self::append_session(&mut url, session);
        self.request_envelope(self.client.get(&url)).await
    }

    /// Start or update navigation. `POST /{type}/{id}/config/navigation?session=<expr>`
    pub async fn set_navigation(&self, device_type: &str, id: &str, session: Option<&str>, config: &NavigationConfigure) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        let mut url = self.config_url(device_type, id, "navigation");
        Self::append_session(&mut url, session);
        self.request_envelope_void(self.client.post(&url).json(config)).await
    }

    /// Stop navigation. `DELETE /{type}/{id}/config/navigation?session=<expr>`
    pub async fn delete_navigation(&self, device_type: &str, id: &str, session: Option<&str>) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        let mut url = self.config_url(device_type, id, "navigation");
        Self::append_session(&mut url, session);
        self.request_envelope_void(self.client.delete(&url)).await
    }
}