Vive Tracker 3.0 OpenXR pogo pin or USB input

Hello, I am trying to get data in from the HTC Vive Tracker 3.0 into Unity. I am using the SteamVR app to connect to the devices.

(optional context): HTC’s tracker 3.0 developer guidelines specify how you can use the pogo pins to send input to an application. Those pogo pins give you four booleans to work with. It also specifies how you can use USB to send additional data to the tracker (three float values and two additional booleans), although it appears that this is only registered by SteamVR if you modify the tracker’s config file so that it tells SteamVR it’s a controller rather than a tracker (see this)

The issue at hand is that I cannot read any data into Unity besides device pose data. I started with this script from “OpenXR and OpenVR together” created by staff member thep3000. Further looking at this script from HTC’s Vive OpenXR github repo, it appears that they got their Ultimate Tracker to work with input, although I don’t have one to test that. Using the above two scripts and also looking at the builtin HTC Vive Controller profile for OpenXR led me to create the following script.

using System;
using System.Collections.Generic;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.XR;
using UnityEngine.Scripting;
using UnityEngine.XR.OpenXR.Input;

#if UNITY_EDITOR
using UnityEditor;
#endif

using PoseControl = UnityEngine.InputSystem.XR.PoseControl;

/// OpenXR HTC vive tracker interaction specification
///     <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_HTCX_vive_tracker_interaction"/>
/// original HTC Vive Tracker Interaction Profile with only device Pose
///     <see href="https://discussions.unity.com/t/openxr-and-openvr-together/841675/21"/>
/// Vive's official OpenXR interaction profile for the Ultimate Tracker
///     <see href="https://github.com/ViveSoftware/VIVE-OpenXR/blob/5ac252bf2edfcbc04d952e183bfaa49d35534b2e/com.htc.upm.vive.openxr/Runtime/Features/Tracker/XR/Scripts/ViveXRTracker.cs"/>

namespace UnityEngine.XR.OpenXR.Features.Interactions
{
    /// <summary>
    /// This <see cref="OpenXRInteractionFeature"/> enables the use of the HTC Vive Tracker interaction profiles in OpenXR
    /// </summary>
#if UNITY_EDITOR
    [UnityEditor.XR.OpenXR.Features.OpenXRFeature(
        UiName = "HTC Vive Tracker OpenXR Profile",
        BuildTargetGroups = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WSA },
        Company = "MASSIVE",
        Desc = "Support for enabling the Vive XR Tracker interaction profile. Registers the controller map for tracker if enabled.",
        DocumentationLink = Constants.k_DocumentationManualURL,
        OpenxrExtensionStrings = extensionName,
        Category = UnityEditor.XR.OpenXR.Features.FeatureCategory.Interaction,
        FeatureId = featureId,
        Version = "0.0.1")]
#endif


    public class ViveTrackerOpenXRProfile : OpenXRInteractionFeature
    {
        /// <summary>
        /// OpenXR specification that supports the vive tracker
        /// <see href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_HTCX_vive_tracker_interaction"/>
        /// </summary>
        public const string extensionName = "XR_HTCX_vive_tracker_interaction";

        /// <summary>
        /// The feature id string. This is used to give the feature a well known id for reference
        /// </summary>
        public const string featureId = "com.massive.openxr.feature.input.htcvivetracker";

        /// <summary>
        /// The interaction profile path for the HTC Vive Tracker in OpenXR
        /// </summary>
        public const string profile = "/interaction_profiles/htc/vive_tracker_htcx";

        /// <summary>
        /// 
        /// </summary>
        private const string kDeviceLocalizedName = "HTC Vive Tracker OpenXR";

        /// <summary>
        /// OpenXR user path definitions for XR_HTCX_vive_tracker_interaction
        /// </summary>
        public struct TrackerUserPaths
        {
            // if you add the below missing paths, also add the path name to commonUsages of XRViveTracker
            // as of the creation of this script, left_wrist, right_wrist, left_ankle, and right_ankle are not supported by SteamVR

            // missing XR_NULL_PATH
            // missing handheld_object
            public const string leftFoot = "/user/vive_tracker_htcx/role/left_foot";
            public const string rightFoot = "/user/vive_tracker_htcx/role/right_foot";
            public const string leftShoulder = "/user/vive_tracker_htcx/role/left_shoulder";
            public const string rightShoulder = "/user/vive_tracker_htcx/role/right_shoulder";
            public const string leftElbow = "/user/vive_tracker_htcx/role/left_elbow";
            public const string rightElbow = "/user/vive_tracker_htcx/role/right_elbow";
            public const string leftKnee = "/user/vive_tracker_htcx/role/left_knee";
            public const string rightKnee = "/user/vive_tracker_htcx/role/right_knee";
            // missing left_wrist
            // missing right_wrist
            // missing left_ankle
            // missing right_ankle
            public const string waist = "/user/vive_tracker_htcx/role/waist";
            public const string chest = "/user/vive_tracker_htcx/role/chest";
            public const string camera = "/user/vive_tracker_htcx/role/camera";
            public const string keyboard = "/user/vive_tracker_htcx/role/keyboard";
        }

        /// <summary>
        /// OpenXR component path definitions for XR_HTCX_vive_tracker_interaction.
        /// Component paths are used by input subsystem to bind physical inputs to actions.
        /// </summary>
        public struct TrackerComponentPaths
        {
            // type PoseControl
            public const string devicepose = "/input/grip/pose";

            // type ButtonControl
            public const string system = "/input/system/click"; // may not be available for application use
            public const string menu = "/input/menu/click";
            public const string trigger = "/input/trigger/click";
            public const string grip = "/input/squeeze/click";
            public const string pad = "/input/trackpad/click";
            public const string padTouch = "/input/trackpad/touch";

            // type AxisControl
            public const string triggerValue = "/input/trigger/value";
            public const string padXValue = "/input/trackpad/x";
            public const string padYValue = "/input/trackpad/y";

            //type HapticControl
            public const string haptic = "/output/haptic";
        }

        /// <summary>
        /// A set of bit flags describing XR.InputDevice characteristics
        /// </summary>
        [Flags]
        public enum InputDeviceTrackerCharacteristics : uint
        {
            TrackerLeftFoot = 0x1000u,
            TrackerRightFoot = 0x2000u,
            TrackerLeftShoulder = 0x4000u,
            TrackerRightShoulder = 0x8000u,
            TrackerLeftElbow = 0x10000u,
            TrackerRightElbow = 0x20000u,
            TrackerLeftKnee = 0x40000u,
            TrackerRightKnee = 0x80000u,
            TrackerWaist = 0x100000u,
            TrackerChest = 0x200000u,
            TrackerCamera = 0x400000u,
            TrackerKeyboard = 0x800000u
        }

        /// <summary>
        /// An Input System device based off the <a href="https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_HTCX_vive_tracker_interaction">HTC Vive Tracker OpenXR specifications
        /// </summary>
        [Preserve, InputControlLayout(displayName = "HTC Vive Tracker (OpenXR)", commonUsages = new[] { "Left Foot", "Right Foot", "Left Shoulder", "Right Shoulder", "Left Elbow", "Right Elbow", "Left Knee", "Right Knee", "Waist", "Chest", "Camera", "Keyboard" })]
        public class ViveTrackerOpenXR : XRControllerWithRumble
        {            
            #region Pose
            /// <summary>
            /// device pose. Contains isTracked, trackingState, position, rotation, velocity, and angularVelocity 
            /// <see cref="TrackerComponentPaths.devicepose"/>
            /// </summary>
            [Preserve, InputControl(offset = 0, aliases = new[] { "device", "entityPose" }, usage = "Device", noisy = true)]
            public PoseControl devicePose { get; private set; }

            /// <summary>
            /// if data is valid, equivalent to devicePose/isTracked
            /// necessary for compatibility with XRSDK layouts
            /// </summary>
            [Preserve, InputControl(offset = 0, usage = "IsTracked")]
            new public ButtonControl isTracked { get; private set; }

            /// <summary>
            /// represents what data is valid, equivalent to devicePose/trackingState
            /// necessary for compatibility with XRSDK layouts
            /// </summary>
            [Preserve, InputControl(offset = 4, usage = "TrackingState")]
            new public IntegerControl trackingState { get; private set; }

            /// <summary>
            /// position of device, equivalent to devicePose/position
            /// necessary for compatibility with XRSDK layouts
            /// </summary>
            [Preserve, InputControl(offset = 8, alias = "gripPosition")]
            new public Vector3Control devicePosition { get; private set; }

            /// <summary>
            /// rotation/orientation of device, equivalent to devicePose/rotation
            /// necessary for compatibility with XRSDK layouts
            /// </summary>
            [Preserve, InputControl(offset = 20, alias = "gripOrientation")]
            new public QuaternionControl deviceRotation { get; private set; }
            #endregion

            #region Boolean Inputs
            /// <summary>
            /// System button on top of tracker. May not be available for application use
            /// <see cref="TrackerComponentPaths.system"/>
            /// </summary>
            [Preserve, InputControl(alias = "systemButton", usage = "SystemButton")]
            public ButtonControl system { get; private set; }

            /// <summary>
            /// Menu button. Accessed on vive tracker 3.0 via USB or by shorting pins 2 and 6
            /// <see cref="TrackerComponentPaths.menu"/>
            /// </summary>
            [Preserve, InputControl(alias = "menuButton", usage = "MenuButton")]
            public ButtonControl menu { get; private set; }

            /// <summary>
            /// Grip button. Accessed on vive tracker 3.0 via USB or by shorting pins 2 and 3
            /// <see cref="TrackerComponentPaths.grip"/>
            /// </summary>
            [Preserve, InputControl(alias = "gripButton", usage = "GripButton")]
            public ButtonControl grip { get; private set; }

            /// <summary>
            /// Trigger button. Accessed on vive tracker 3.0 via USB or by shorting pins 2 and 4
            /// <see cref="TrackerComponentPaths.trigger"/>
            /// </summary>
            [Preserve, InputControl(alias = "triggerButton", usage = "TriggerButton")]
            public ButtonControl trigger { get; private set; }

            /// <summary>
            /// Trackpad button. Accessed on vive tracker 3.0 via USB or by shorting pins 2 and 5
            /// <see cref="TrackerComponentPaths.pad"/>
            /// </summary>
            [Preserve, InputControl(alias = "trackpadButton", usage = "TrackpadButton")]
            public ButtonControl pad { get; private set; }

            /// <summary>
            /// Trackpad touch. Only accessible on vive tracker 3.0 via USB
            /// <see cref="TrackerComponentPaths.padTouch"/>
            /// </summary>
            [Preserve, InputControl(alias = "trackpadTouch", usage = "TrackpadTouch")]
            public ButtonControl padTouch { get; private set; }
            #endregion

            #region Float Inputs
            /// <summary>
            /// Trigger pull analog value. Only accessible on vive tracker 3.0 via USB
            /// <see cref="TrackerComponentPaths.triggerValue"/>
            /// </summary>
            [Preserve, InputControl(alias = "triggerValue", usage = "TriggerValue")]
            public AxisControl triggerValue { get; private set; }

            /// <summary>
            /// Trackpad X/horizontal analog value. Only accessible on vive tracker 3.0 via USB
            /// <see cref="TrackerComponentPaths.padXValue"/>
            /// </summary>
            [Preserve, InputControl(alias = "trackpadXValue", usage = "TrackpadXValue")]
            public AxisControl padXValue { get; private set; }

            /// <summary>
            /// Trackpad Y/vertical analog value. Only accessible on vive tracker 3.0 via USB
            /// <see cref="TrackerComponentPaths.padYValue"/>
            /// </summary>
            [Preserve, InputControl(alias = "trackpadYValue", usage = "TrackpadYValue")]
            public AxisControl padYValue { get; private set; }
            #endregion

            #region device Outputs
            /// <summary>
            /// Haptic outputs. Accessed on vive tracker 3.0 by pogo pin 1. Untested via USB
            /// <see cref="TrackerComponentPaths.haptic"/>
            /// </summary>
            [Preserve, InputControl(usage = "Haptic")]
            public HapticControl haptic { get; private set; }
            #endregion

            /// <inheritdoc cref="OpenXRDevice"/>
            protected override void FinishSetup() {
                base.FinishSetup();

                devicePose = GetChildControl<PoseControl>("devicePose");
                isTracked = GetChildControl<ButtonControl>("isTracked");
                trackingState = GetChildControl<IntegerControl>("trackingState");
                devicePosition = GetChildControl<Vector3Control>("devicePosition");
                deviceRotation = GetChildControl<QuaternionControl>("deviceRotation");

                system = GetChildControl<ButtonControl>("system");
                menu = GetChildControl<ButtonControl>("menu");
                grip = GetChildControl<ButtonControl>("grip");
                trigger = GetChildControl<ButtonControl>("trigger");
                pad = GetChildControl<ButtonControl>("pad");
                padTouch = GetChildControl<ButtonControl>("padTouch");

                triggerValue = GetChildControl<AxisControl>("triggerValue");
                padXValue = GetChildControl<AxisControl>("padXValue");
                padYValue = GetChildControl<AxisControl>("padYValue");

                haptic = GetChildControl<HapticControl>("haptic");

                var deviceDescriptor = XRDeviceDescriptor.FromJson(description.capabilities);

                if ((deviceDescriptor.characteristics & (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerLeftFoot) != 0)
                    InputSystem.InputSystem.SetDeviceUsage(this, "Left Foot");
                else if ((deviceDescriptor.characteristics & (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerRightFoot) != 0)
                    InputSystem.InputSystem.SetDeviceUsage(this, "Right Foot");
                else if ((deviceDescriptor.characteristics & (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerLeftShoulder) != 0)
                    InputSystem.InputSystem.SetDeviceUsage(this, "Left Shoulder");
                else if ((deviceDescriptor.characteristics & (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerRightShoulder) != 0)
                    InputSystem.InputSystem.SetDeviceUsage(this, "Right Shoulder");
                else if ((deviceDescriptor.characteristics & (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerLeftElbow) != 0)
                    InputSystem.InputSystem.SetDeviceUsage(this, "Left Elbow");
                else if ((deviceDescriptor.characteristics & (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerRightElbow) != 0)
                    InputSystem.InputSystem.SetDeviceUsage(this, "Right Elbow");
                else if ((deviceDescriptor.characteristics & (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerLeftKnee) != 0)
                    InputSystem.InputSystem.SetDeviceUsage(this, "Left Knee");
                else if ((deviceDescriptor.characteristics & (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerRightKnee) != 0)
                    InputSystem.InputSystem.SetDeviceUsage(this, "Right Knee");
                else if ((deviceDescriptor.characteristics & (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerWaist) != 0)
                    InputSystem.InputSystem.SetDeviceUsage(this, "Waist");
                else if ((deviceDescriptor.characteristics & (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerChest) != 0)
                    InputSystem.InputSystem.SetDeviceUsage(this, "Chest");
                else if ((deviceDescriptor.characteristics & (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerCamera) != 0)
                    InputSystem.InputSystem.SetDeviceUsage(this, "Camera");
                else if ((deviceDescriptor.characteristics & (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerKeyboard) != 0)
                    InputSystem.InputSystem.SetDeviceUsage(this, "Keyboard");
                else {
                    Debug.Log("No tracker role could be found that matches this device");
                }

                Debug.Log("Device added");
            }
        }

        /// <summary>
        /// Registers the <see cref="ViveTracker"/> layout with the Input System.
        /// </summary>
        protected override void RegisterDeviceLayout() {
            InputSystem.InputSystem.RegisterLayout(typeof(ViveTrackerOpenXR),
                        matches: new InputDeviceMatcher()
                        .WithInterface(XRUtilities.InterfaceMatchAnyVersion)
                        .WithProduct(kDeviceLocalizedName));
        }

        /// <summary>
        /// Removes the <see cref="ViveTracker"/> layout from the Input System.
        /// </summary>
        protected override void UnregisterDeviceLayout() {
            InputSystem.InputSystem.RemoveLayout(nameof(ViveTrackerOpenXR));
        }

        /// <inheritdoc/>
        protected override void RegisterActionMapsWithRuntime() {
            ActionMapConfig actionMap = new ActionMapConfig()
            {
                name = "htcvivetracker",
                localizedName = kDeviceLocalizedName,
                desiredInteractionProfile = profile,
                manufacturer = "HTC",
                serialNumber = "",
                deviceInfos = new List<DeviceConfig>()
                {
                    new DeviceConfig()
                    {
                        characteristics = (InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerLeftFoot),
                        userPath = TrackerUserPaths.leftFoot
                    },
                    new DeviceConfig()
                    {
                        characteristics = (InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerRightFoot),
                        userPath = TrackerUserPaths.rightFoot
                    },
                    new DeviceConfig()
                    {
                        characteristics = (InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerLeftShoulder),
                        userPath = TrackerUserPaths.leftShoulder
                    },
                    new DeviceConfig()
                    {
                        characteristics = (InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerRightShoulder),
                        userPath = TrackerUserPaths.rightShoulder
                    },
                    new DeviceConfig()
                    {
                        characteristics = (InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerLeftElbow),
                        userPath = TrackerUserPaths.leftElbow
                    },
                    new DeviceConfig()
                    {
                        characteristics = (InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerRightElbow),
                        userPath = TrackerUserPaths.rightElbow
                    },
                    new DeviceConfig()
                    {
                        characteristics = (InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerLeftKnee),
                        userPath = TrackerUserPaths.leftKnee
                    },
                    new DeviceConfig()
                    {
                        characteristics = (InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerRightKnee),
                        userPath = TrackerUserPaths.rightKnee
                    },
                    new DeviceConfig()
                    {
                        characteristics = (InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerWaist),
                        userPath = TrackerUserPaths.waist
                    },
                    new DeviceConfig()
                    {
                        characteristics = (InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerChest),
                        userPath = TrackerUserPaths.chest
                    },
                    new DeviceConfig()
                    {
                        characteristics = (InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerCamera),
                        userPath = TrackerUserPaths.camera
                    },
                    new DeviceConfig()
                    {
                        characteristics = (InputDeviceCharacteristics.TrackedDevice | InputDeviceCharacteristics.Controller | (InputDeviceCharacteristics)InputDeviceTrackerCharacteristics.TrackerKeyboard),
                        userPath = TrackerUserPaths.keyboard
                    }
                },
                actions = new List<ActionConfig>()
                {
                    // Device Pose
                    new ActionConfig()
                    {
                        name = "devicePose",
                        localizedName = "Grip Pose",
                        type = ActionType.Pose,
                        usages = new List<string>() { "Device" },
                        bindings = new List<ActionBinding>()
                        {
                            new ActionBinding()
                            {
                                interactionPath = TrackerComponentPaths.devicepose,
                                interactionProfileName = profile,
                            }
                        }
                    },
                    // System button
                    new ActionConfig()
                    {
                        name = "system",
                        localizedName = "System",
                        type = ActionType.Binary,
                        usages = new List<string>() { "SystemButton" },
                        bindings = new List<ActionBinding>()
                        {
                            new ActionBinding()
                            {
                                interactionPath = TrackerComponentPaths.system,
                                interactionProfileName = profile,
                            }
                        }
                    },
                    // Menu button
                    new ActionConfig()
                    {
                        name = "menu",
                        localizedName = "Menu",
                        type = ActionType.Binary,
                        usages = new List<string>() { "MenuButton" },
                        bindings = new List<ActionBinding>()
                        {
                            new ActionBinding()
                            {
                                interactionPath = TrackerComponentPaths.menu,
                                interactionProfileName = profile,
                            }
                        }
                    },
                    // Grip button
                    new ActionConfig()
                    {
                        name = "grip",
                        localizedName = "Grip",
                        type = ActionType.Binary,
                        usages = new List<string>() { "GripButton" },
                        bindings = new List<ActionBinding>()
                        {
                            new ActionBinding()
                            {
                                interactionPath = TrackerComponentPaths.grip,
                                interactionProfileName = profile,
                            }
                        }
                    },
                    // Trigger button
                    new ActionConfig()
                    {
                        name = "trigger",
                        localizedName = "Trigger",
                        type = ActionType.Binary,
                        usages = new List<string>()  { "TriggerButton" },
                        bindings = new List<ActionBinding>()
                        {
                            new ActionBinding()
                            {
                                interactionPath = TrackerComponentPaths.trigger,
                                interactionProfileName = profile,
                            }
                        }
                    },
                    // Trackpad button
                    new ActionConfig()
                    {
                        name = "pad",
                        localizedName = "Pad",
                        type = ActionType.Binary,
                        usages = new List<string>() { "TrackpadButton" },
                        bindings = new List<ActionBinding>()
                        {
                            new ActionBinding()
                            {
                                interactionPath = TrackerComponentPaths.pad,
                                interactionProfileName = profile,
                            }
                        }
                    },
                    // Trackpad touch
                    new ActionConfig()
                    {
                        name = "padTouch",
                        localizedName = "PadTouch",
                        type = ActionType.Binary,
                        usages = new List<string>() { "TrackpadTouch" },
                        bindings = new List<ActionBinding>()
                        {
                            new ActionBinding()
                            {
                                interactionPath = TrackerComponentPaths.padTouch,
                                interactionProfileName = profile,
                            }
                        }
                    },
                    // Trigger pull analog value
                    new ActionConfig()
                    {
                        name = "triggerValue",
                        localizedName = "TriggerValue",
                        type = ActionType.Axis1D,
                        usages = new List<string>() { "TrackpadValue" },
                        bindings = new List<ActionBinding>()
                        {
                            new ActionBinding()
                            {
                                interactionPath = TrackerComponentPaths.triggerValue,
                                interactionProfileName = profile,
                            }
                        }
                    },
                    // Trackpad X/horizontal analog value
                    new ActionConfig()
                    {
                        name = "padXValue",
                        localizedName = "Trackpad X Value",
                        type = ActionType.Axis1D,
                        usages = new List<string>() { "TrackpadXValue" },
                        bindings = new List<ActionBinding>()
                        {
                            new ActionBinding()
                            {
                                interactionPath = TrackerComponentPaths.padXValue,
                                interactionProfileName = profile,
                            }
                        }
                    },
                    // Trackpad Y/vertical analog value
                    new ActionConfig()
                    {
                        name = "padYValue",
                        localizedName = "Trackpad Y Value",
                        type = ActionType.Axis1D,
                        usages = new List<string>() { "TrackpadYValue" },
                        bindings = new List<ActionBinding>()
                        {
                            new ActionBinding()
                            {
                                interactionPath = TrackerComponentPaths.padYValue,
                                interactionProfileName = profile,
                            }
                        }
                    },
                    // Haptic output
                    new ActionConfig()
                    {
                        name = "haptic",
                        localizedName = "Haptic Output",
                        type = ActionType.Vibrate,
                        usages = new List<string>() { "Haptic" },
                        bindings = new List<ActionBinding>()
                        {
                            new ActionBinding()
                            {
                                interactionPath = TrackerComponentPaths.haptic,
                                interactionProfileName = profile,
                            }
                        }
                    }
                }
            };

            AddActionMap(actionMap);
        }

        protected override bool OnInstanceCreate(ulong xrInstance) {
            bool res = base.OnInstanceCreate(xrInstance);

            Debug.Log(extensionName + " version " + OpenXRRuntime.GetExtensionVersion(extensionName));

            string debug = "HTC Vive Tracker OpenXR Extension ";
            if (OpenXRRuntime.IsExtensionEnabled(extensionName)) {
                Debug.Log(debug + "Enabled");
            }
            else {
                Debug.LogWarning(debug + "Not Enabled");
            }

            return res;
        }
    }
}

However, this script doesn’t actually read in anything other than the pose data that worked earlier with thep3000’s script. I believe a potential reason for this is that it appears Unity only supports version 1 of OpenXR’s XR_HTCX_vive_tracker_interaction extension. OpenXR’s changelog shows that all three vive tracker revisions were internal merges, which means I cannot see the contents as I am not an employee of a company in the Khronos group.

Thus, were all of the component paths other than /input/grip/pose supported in v1? If so, are there any issues with my code and how it’s grabbing the data?

If component paths were supported in v1, were those component paths supported in Unity’s implementation of the OpenXR plugin?

If the component paths were implemented later in v2 or v3, are there any plans in the short term to implement that or should I look for alternative solutions for the time being?

1 Like

I believe that the SteamVR modification means that the trackers won’t behave as trackers at all, and might end up coming through to the runtime as normal Vive controllers instead. You won’t be able to do any more than the official input binding, and OpenXR will perform a best fit approach.

FYI internal merges just mean that they were done in the Khronos group Gitlab rather than the public GitHub; to be clear each publish of the spec is basically a snapshot of the internal repo state, so the one you see in the specification is the latest published version. I don’t think those pogo pins are currently addressable on the tracker input extension.

1 Like

Yeah, I’m hoping to not have to resort to changing the configuration files at all. It’s clunky and makes the setup process for other users much more difficult. What I was getting at there is that 1) if the tracker sends all its data regardless, I’m guessing SteamVR just tosses the axis data. If true, my issue should be with SteamVR. If false, then my issue remains with Unity and it not correctly grabbing the data via OpenXR. 2) if the tracker looks at its own config files before determining what data to send and only sends the axis data if its set as a controller, then my issue lies with Vive.

As of now with further research, it looks like the issue lies with SteamVR and that it doesn’t forward any data to other applications beyond the device pose if the tracker is set to anything other than “held in hand.” I can bypass this with a clunky C++ program that uses OpenVR to grab the tracker ControllerState, which I then send via UDP to Unity. I really don’t like this as it’s very clunky and doesn’t work all the time, which is very frustrating. Ideally, SteamVR would just remove these limits and allow me to grab any input data from any role at any time.

What I was getting at with this is that I can’t see exactly what the internal merge contained. All I can really do is blame the OpenXR xml file and see when each line was changed most recently. Like I said earlier though, I think the issue lies with SteamVR and not Vive or Unity.

2 Likes

Update: According to Valve employee danw, pogo pin/usb input has been implemented in the latest version of SteamVR (I’m using 2.9.4), and I have confirmed the project works on my machine.

However, I haven’t been able to get it working in Unity with this github repo. Instructions to replicate are in the readme.

If anyone has anything obvious I’m missing with my code, please feel free to let me know. The issue is probably in my code, as I don’t think it’s an issue with Unity since their Input System is so device-agnostic, but it could be that they need to implement something on their end as well.

1 Like

I still have not been able to get this to work. I can confirm that danw’s project works on my machine and I can grab menu, grip, and trigger booleans via OpenXR in the c++ script, but I still can’t get anything into Unity. I hope this can get resolved, as pogo pin and other input through the tracker has been an ongoing issue in OpenXR implementation for several years now

1 Like

I am also seeming to have this issue. Have you found any successful fixes?

Still no fixes for this issue it seems. No matter what I do the Vive tracker pogo pin inputs are not reflected in Unity but do show up in SteamVR. Exactly as @e_kernen2 points out here