Skip to main content

Device Information Messages

Messages that convey information about devices currently connected to the system. All of the following messages are sent Server -> Client, either in response to RequestDeviceList or on connection/disconnection of a device.


DeviceList

Description: Server reply to a client request for a device list, or sent as an event when a device is connected or disconnected.

Introduced In Spec Version: 0

Last Updated In Spec Version: 4 (See Deprecated Messages for older versions.)

Fields:

  • Id (unsigned int): Message Id
  • Devices (map of indexes to device object, with each object having the following fields):
    • DeviceName (string): Descriptive name of the device, as taken from the base device configuration file.
    • DeviceIndex (unsigned integer): Index used to identify the device when sending Device Messages.
      • This is a repeat of the map key
    • DeviceMessageTimingGap (unsigned integer): Recommended minimum gap between device commands, in milliseconds, TO BE ENFORCED ON THE SERVER. If multiple messages are sent within the timespan defined here, only the latest commands will be sent on the next message trigger. This relieves developers of having to regulate input from users or tune their clients, at the cost of added server complexity. As no one but the Buttplug Core Team writes servers, that means you're getting less work for free here. If this is set to 0, it means there is no minimum update rate (if there is a device updating at after that 1khz in buttplug, please get in touch, I have concerns).
    • DeviceDisplayName (optional, string): User provided display name for a device. Useful for cases where a users may have multiple of the same device connected. Optional field, not required to be included in message. Missing value means that no device display name is set, and device name should be used.
    • DeviceFeatures (map of indexes to feature objects, with each object having the following fields)
      • Description (string): Text descriptor for a feature.
      • FeatureIndex (unsigned 32-bit integer): Index that should be used to refer to the feature in messages like ValueCmd, SensorReadCmd, etc...
        • This is a repeat of the map key.
      • Output (Object, may be null): Represents an outputs that are part of this feature. A map of OutputType to information objects.
        • [OutputType] (OutputType as String): OutputType is used as a key here, so this field would be something like "Vibrate", "Position", etc...
          • StepCount (unsigned 32-bit integer): Number of steps usable for this output type. For instance, if a vibrator has 20 steps of speed control, StepCount will be 20. However, if a user has reduced the usable range in their server config, it may be < 20.
      • Input (Object, may be null): Represents a sensor that may be part of this feature. A map of InputType to information objects.
        • [InputType] (InputType as String): InputType is used as a key here, so this field would be something like "Battery", "Pressure", etc...
          • InputCommandType (array of strings): Some combination of "Read" and/or "Subscribe".
          • ValueRange (Range, array of 2 signed 32-bit integer values): Range of values that may be received from the sensor, if known.
Why are the DeviceIndex and FeatureIndex repeated as map keys and an object fields?

DeviceIndex and FeatureIndex are how client implementations refer to a device in InputCmd and OutputCmd messages. They are the main identifiers for Buttplug. In most client implementations we've built so far, we end up using Map<number, object> types to represent Devices and Features, mapping indexes to the related objects. However, for the objects themselves, it tends be to handy for the object to know its index when forming device control messages.

With client ergonomics in mind, we just pack device info this way to begin with, so that serialization can happen from our base storage structures, and deserialization gives us the type of data structures we usually had to build by iterating through object arrays in past versions. This also gives us the added bonus of not being able to somehow pack devices with matching IDs (which would be a massive bug anyways but now it's not even structurally possible.).

There is some awkwardness in the JSON implementation of this, as object field names cannot be numeric. These are normally converted to strings when serialized, then back to numeric types automatically when deserialized for whatever language a client may be implemented in, assuming it has a decent serde library.

For those screaming "BUT ADDED SIZE AND REDUNDANCY AND YOU COULD STILL SOMEHOW PACK KEYS THAT DON'T MATCH INTERNAL INDEX FIELDS": DeviceList messages are sent a few times a minutes, so size doesn't matter. We could screw up consistency, but once again that'd be a huge bug and there's been one server implementation for 8 years.

Expected Response:

None. Server-to-Client message only.

Flow Diagram:

Serialization Example:

[
{
"DeviceList": {
"Id": 1,
"Devices": {
"0": {
"DeviceName": "Test Vibrator",
"DeviceIndex": 0,
"Features": {
"0": {
"FeatureIndex": 0,
"Descriptor": "Clitoral Stimulator",
"Output": {
"Vibrate": {
"StepCount": 20
}
}
},
"1": {
"FeatureIndex": 1,
"Descriptor": "Insertable Stimulator",
"Output": {
"Vibrate": {
"StepCount": 20
}
}
},
"2": {
"FeatureIndex": 2,
"Descriptor": "Battery",
"Input": {
"Battery": {
"ValueRange": [0, 100],
"InputCommandType": ["Read"]
}
}
}
}
},
"1": {
"DeviceName": "Test Stroker",
"DeviceIndex": 1,
"DeviceMessageTimingGap": 100,
"DeviceDisplayName": "User set name",
"Features": {
"0": {
"FeatureIndex": 0,
"Descriptor": "Stroker",
"Output": {
"PositionWithDuration": {
"StepCount": 100
}
},
"Input": {
"Position": {
"ValueRange": [0, 100],
"InputCommandType": ["Read", "Subscribe"]
}
}
},
"1": {
"FeatureIndex": 1,
"Descriptor": "Bluetooth Radio RSSI",
"Input": {
"RSSI": {
"ValueRange": [-10, -100],
"InputCommandType": ["Read"]
}
}
}
}
}
}
}
}
]