values = new ArrayList<>();
- GenericMessageAttributes gattrs = (GenericMessageAttributes) attrs;
- for (GenericFeatureAttributes attr : gattrs.getFeatures()) {
- values.add(null);
+ if (o == null || getClass() != o.getClass()) {
+ return false;
}
-
- for (int index = 0; index < vectors.length; index++) {
- if (index >= gattrs.getFeatures().size()) {
- throw new ButtplugDeviceException("Device doesn't have a LinearCmd feature at index " + index + "!");
- }
- values.set(index, new LinearCmd.LinearSubCmd(index, vectors[index].getLeft(), vectors[index].getRight()));
+ ButtplugClientDevice that = (ButtplugClientDevice) o;
+ if (deviceIndex != that.deviceIndex ||
+ !deviceName.equals(that.deviceName) ||
+ !deviceDisplayName.equals(that.deviceDisplayName) ||
+ !Objects.equals(deviceMessageTimingGap, that.deviceMessageTimingGap) ||
+ (deviceFeatures == null) != (that.deviceFeatures == null)) {
+ return false;
}
-
- return client.sendDeviceMessage(this, new LinearCmd(getDeviceIndex(),
- values.toArray(new LinearCmd.LinearSubCmd[]{}), client.getNextMsgId()));
- }
-
- /**
- * Sends a command to read a sensor value from the device.
- *
- * This method constructs and sends a sensor read command to the device for the specified sensor type and index.
- * It checks if the device supports the "SensorReadCmd" attribute before attempting to send the command.
- * If the device does not support this attribute, or if the command cannot be sent, a {@link ButtplugDeviceException}
- * is thrown.
- *
- *
- * @param sensorIndex The index of the sensor feature to read. This value specifies which sensor data
- * to retrieve from the device.
- * @param sensorType The type of sensor to read (e.g., "Battery"). This value indicates the specific
- * type of sensor data requested.
- *
- * @return A {@link Future} representing the pending result of the sensor read command.
- * Once completed, the {@link ButtplugMessage} returned by the Future will contain
- * the sensor data if the command succeeds.
- *
- * @throws ButtplugDeviceException if the device does not support "SensorReadCmd" or if
- * the sensor read command could not be created or sent.
- */
- public final Future sendSensorReadCmd(final int sensorIndex, final String sensorType)
- throws ButtplugDeviceException {
-
- MessageAttributes attrs = getDeviceMessages().get("SensorReadCmd");
- if (!(attrs instanceof SensorMessageAttributes)) {
- throw new ButtplugDeviceException("Device doesn't support SensorReadCmd!");
+ if (deviceFeatures == null) {
+ return true;
}
-
- final SensorReadCmd cmd = new SensorReadCmd(this.deviceIndex, client.getNextMsgId());
- cmd.setSensorType(sensorType);
- cmd.setSensorIndex(sensorIndex);
-
- return client.sendDeviceMessage(this, cmd);
- }
-
- /**
- * Checks if the device has a battery sensor feature.
- *
- * This method verifies if the device's message attributes include a "Battery" sensor feature
- * by looking for the presence of "SensorReadCmd" in the device's messages. If found,
- * it then checks if one of the sensor features corresponds to "Battery".
- *
- *
- * @return {@code true} if the device has a "Battery" sensor feature, {@code false} otherwise.
- */
- public final boolean hasBatterySensor() {
- MessageAttributes attrs = getDeviceMessages().get("SensorReadCmd");
- if (!(attrs instanceof SensorMessageAttributes)) {
+ if (deviceFeatures.size() != that.deviceFeatures.size() ||
+ !deviceFeatures.keySet().containsAll(that.deviceFeatures.keySet())) {
return false;
}
-
- SensorMessageAttributes sensorAttrs = (SensorMessageAttributes) attrs;
-
- boolean hasBatteryLevel = sensorAttrs.getFeatures().stream().anyMatch(
- featureAttributes -> "Battery".equals(featureAttributes.getSensorType())
- );
-
- return hasBatteryLevel;
- }
-
- /**
- * Reads the battery level of the device.
- *
- * This method queries the device to retrieve its battery level as a percentage from 0 to 100.
- * Before calling this method, use {@link #hasBatterySensor()} to verify that the device supports
- * a "Battery" sensor feature. If the device lacks this feature or returns an unexpected message type,
- * an exception is thrown.
- *
- *
- * @return The battery level of the device, in the range of 0 to 100.
- *
- * @throws ButtplugDeviceException if the device does not support "SensorReadCmd",
- * does not have a "Battery" feature, or returns an invalid response.
- * @throws InterruptedException if the thread is interrupted while waiting for a response from the device.
- * @throws ExecutionException if an exception occurred during the execution of the sensor read command.
- * @throws TimeoutException if the response from the device took more than 2 seconds
- */
- public final long readBatteryLevel() throws ButtplugDeviceException, InterruptedException, ExecutionException, TimeoutException {
- MessageAttributes attrs = getDeviceMessages().get("SensorReadCmd");
- if (!(attrs instanceof SensorMessageAttributes)) {
- throw new ButtplugDeviceException("Device doesn't support SensorReadCmd!");
- }
-
- SensorMessageAttributes sensorAttrs = (SensorMessageAttributes) attrs;
- int index = -1;
- boolean found = false;
- for (SensorFeatureAttributes featureAttributes : sensorAttrs.getFeatures()) {
- index++;
- if ("Battery".equals(featureAttributes.getSensorType())) {
- found = true;
- break;
+ for (Integer feat : deviceFeatures.keySet()) {
+ if (!deviceFeatures.get(feat).equals(that.deviceFeatures.get(feat))) {
+ return false;
}
}
-
- if (!found) {
- throw new ButtplugDeviceException("Device doesn't have Battery feature!");
- }
-
- Future sensorReadFuture = sendSensorReadCmd(index, "Battery");
- ButtplugMessage message = sensorReadFuture.get(2, TimeUnit.SECONDS);
- if (!(message instanceof SensorReading)) {
- throw new ButtplugDeviceException("Invalid ButtplugMessage returned. Expecting SensorReading and got " + message.getClass());
- }
-
- SensorReading sensorReading = (SensorReading) message;
- byte singleByte = sensorReading.getData()[0];
- long result = (singleByte & 0xFF);
- return result;
- }
-
- public final long getRotateCount() {
- return getFeatureCount("RotateCmd");
- }
-
- public final long getLinearCount() {
- return getFeatureCount("LinearCmd");
- }
-
- private long getFeatureCount(final String command) {
- MessageAttributes attrs = getDeviceMessages().get(command);
- if (!(attrs instanceof GenericMessageAttributes)) {
- return 0;
- }
- GenericMessageAttributes gattrs = (GenericMessageAttributes) attrs;
- return gattrs.getFeatures().size();
- }
-
- public final long getDeviceIndex() {
- return deviceIndex;
- }
-
- public final String getName() {
- return name;
- }
-
- public final String getDisplayName() {
- return displayName;
- }
-
- public final Integer getMessageTimingGap() {
- return messageTimingGap;
- }
-
- public final Map getDeviceMessages() {
- return deviceMessages;
+ return true;
}
}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/ButtplugClientDeviceFeature.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/ButtplugClientDeviceFeature.java
new file mode 100644
index 0000000..119e414
--- /dev/null
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/ButtplugClientDeviceFeature.java
@@ -0,0 +1,258 @@
+package io.github.blackspherefollower.buttplug4j.client;
+
+import io.github.blackspherefollower.buttplug4j.protocol.ButtplugMessage;
+import io.github.blackspherefollower.buttplug4j.protocol.messages.DeviceFeature;
+import io.github.blackspherefollower.buttplug4j.protocol.messages.InputCmd;
+import io.github.blackspherefollower.buttplug4j.protocol.messages.InputCommandType;
+import io.github.blackspherefollower.buttplug4j.protocol.messages.OutputCmd;
+
+import java.util.HashMap;
+import java.util.concurrent.Future;
+
+public class ButtplugClientDeviceFeature {
+
+ private final ButtplugClientDevice device;
+ private final String description;
+ private final HashMap output;
+ private final HashMap input;
+ private final int featureIndex;
+
+ public ButtplugClientDeviceFeature(final ButtplugClientDevice device, final DeviceFeature feature) {
+ this.device = device;
+ this.featureIndex = feature.getFeatureIndex();
+ this.description = feature.getFeatureDescription();
+ this.output = new HashMap<>();
+ if (feature.getOutput() != null) {
+ feature.getOutput().forEach(outputDescriptor -> this.output.put(outputDescriptor.getClass().getSimpleName(), outputDescriptor));
+ }
+ this.input = new HashMap<>();
+ if (feature.getInput() != null) {
+ feature.getInput().forEach(inputDescriptor -> this.input.put(inputDescriptor.getClass().getSimpleName(), inputDescriptor));
+ }
+ }
+
+ private int GetStepFromFloat(final String type, final float value) throws ButtplugDeviceFeatureException {
+ if (value < 0.0f || value > 1.0f) {
+ throw new ButtplugDeviceFeatureException("Range error");
+ }
+ DeviceFeature.OutputDescriptor desc = output.get(type);
+ if (desc == null) {
+ throw new ButtplugDeviceFeatureException(type);
+ }
+ if (desc instanceof DeviceFeature.SteppedOutputDescriptor) {
+ double steps = ((DeviceFeature.SteppedOutputDescriptor) desc).getValue()[1];
+ steps *= value;
+ return (int) Math.floor(steps);
+ } else if (desc instanceof DeviceFeature.PositionWithDuration) {
+ double steps = ((DeviceFeature.PositionWithDuration) desc).getPosition()[1];
+ steps *= value;
+ return (int) Math.floor(steps);
+ } else {
+ throw new ButtplugDeviceFeatureException(type);
+ }
+ }
+
+ private void CheckStepRange(final String type, final float value) throws ButtplugDeviceFeatureException {
+ DeviceFeature.OutputDescriptor desc = output.get(type);
+ if (desc == null) {
+ throw new ButtplugDeviceFeatureException(type);
+ }
+ if (desc instanceof DeviceFeature.SteppedOutputDescriptor) {
+ int steps = ((DeviceFeature.SteppedOutputDescriptor) desc).getValue()[1];
+ if (value > steps || value < 0) {
+ throw new ButtplugDeviceFeatureException("Range error");
+ }
+ } else if (desc instanceof DeviceFeature.PositionWithDuration) {
+ int steps = ((DeviceFeature.PositionWithDuration) desc).getPosition()[1];
+ if (value > steps || value < 0) {
+ throw new ButtplugDeviceFeatureException("Range error");
+ }
+ } else {
+ throw new ButtplugDeviceFeatureException(type);
+ }
+ }
+
+ public boolean HasVibrate() {
+ return output.get("Vibrate") != null;
+ }
+
+ public Future Vibrate(final int vibrate) throws ButtplugDeviceFeatureException {
+ CheckStepRange("Vibrate", vibrate);
+ return device.sendOutputCommand(featureIndex, new OutputCmd.Vibrate(vibrate));
+ }
+
+ public Future VibrateFloat(final float vibrate) throws ButtplugDeviceFeatureException {
+ return device.sendOutputCommand(featureIndex, new OutputCmd.Vibrate(GetStepFromFloat("Vibrate", vibrate)));
+ }
+
+ public boolean HasRotate() {
+ return output.get("Rotate") != null;
+ }
+
+ public Future Rotate(final int rotate) throws ButtplugDeviceFeatureException {
+ CheckStepRange("Rotate", rotate);
+ return device.sendOutputCommand(featureIndex, new OutputCmd.Rotate(rotate));
+ }
+
+ public Future RotateFloat(final float rotate) throws ButtplugDeviceFeatureException {
+ return device.sendOutputCommand(featureIndex, new OutputCmd.Rotate(GetStepFromFloat("Rotate", rotate)));
+ }
+
+ public boolean HasConstrict() {
+ return output.get("Constrict") != null;
+ }
+
+ public Future Constrict(final int constrict) throws ButtplugDeviceFeatureException {
+ CheckStepRange("Constrict", constrict);
+ return device.sendOutputCommand(featureIndex, new OutputCmd.Constrict(constrict));
+ }
+
+ public Future ConstrictFloat(final float constrict) throws ButtplugDeviceFeatureException {
+ return device.sendOutputCommand(featureIndex, new OutputCmd.Constrict(GetStepFromFloat("Constrict", constrict)));
+ }
+
+ public boolean HasSpray() {
+ return output.get("Spray") != null;
+ }
+
+ public Future Spray(final int spray) throws ButtplugDeviceFeatureException {
+ CheckStepRange("Spray", spray);
+ return device.sendOutputCommand(featureIndex, new OutputCmd.Spray(spray));
+ }
+
+ public Future SprayFloat(final float spray) throws ButtplugDeviceFeatureException {
+ return device.sendOutputCommand(featureIndex, new OutputCmd.Spray(GetStepFromFloat("Spray", spray)));
+ }
+
+ public boolean HasPosition() {
+ return output.get("Position") != null;
+ }
+
+ public Future Position(final int position) throws ButtplugDeviceFeatureException {
+ CheckStepRange("Position", position);
+ return device.sendOutputCommand(featureIndex, new OutputCmd.Position(position));
+ }
+
+ public Future PositionFloat(final float position) throws ButtplugDeviceFeatureException {
+ return device.sendOutputCommand(featureIndex, new OutputCmd.Position(GetStepFromFloat("Position", position)));
+ }
+
+ public boolean HasPositionWithDuration() {
+ return output.get("PositionWithDuration") != null;
+ }
+
+ public Future PositionWithDuration(final int position, final int duration) throws ButtplugDeviceFeatureException {
+ CheckStepRange("PositionWithDuration", position);
+ return device.sendOutputCommand(featureIndex, new OutputCmd.PositionWithDuration(position, duration));
+ }
+
+ public Future PositionWithDurationFloat(final float position, final int duration) throws ButtplugDeviceFeatureException {
+ return device.sendOutputCommand(featureIndex, new OutputCmd.PositionWithDuration(GetStepFromFloat("PositionWithDuration", position), duration));
+ }
+
+ public boolean HasLed() {
+ return output.get("Led") != null;
+ }
+
+ public Future Led(final int led) throws ButtplugDeviceFeatureException {
+ CheckStepRange("Led", led);
+ return device.sendOutputCommand(featureIndex, new OutputCmd.Led(led));
+ }
+
+ public Future LedFloat(final float led) throws ButtplugDeviceFeatureException {
+ return device.sendOutputCommand(featureIndex, new OutputCmd.Led(GetStepFromFloat("Led", led)));
+ }
+
+ public boolean HasOscillate() {
+ return output.get("Oscillate") != null;
+ }
+
+ public Future Oscillate(final int oscillate) throws ButtplugDeviceFeatureException {
+ CheckStepRange("Oscillate", oscillate);
+ return device.sendOutputCommand(featureIndex, new OutputCmd.Oscillate(oscillate));
+ }
+
+ public Future OscillateFloat(final float oscillate) throws ButtplugDeviceFeatureException {
+ return device.sendOutputCommand(featureIndex, new OutputCmd.Oscillate(GetStepFromFloat("Oscillate", oscillate)));
+ }
+
+ public boolean HasTemperature() {
+ return output.get("Temperature") != null;
+ }
+
+ public Future Temperature(final int temperature) throws ButtplugDeviceFeatureException {
+ CheckStepRange("Temperature", temperature);
+ return device.sendOutputCommand(featureIndex, new OutputCmd.Temperature(temperature));
+ }
+
+ public Future TemperatureFloat(final float temperature) throws ButtplugDeviceFeatureException {
+ return device.sendOutputCommand(featureIndex, new OutputCmd.Temperature(GetStepFromFloat("Temperature", temperature)));
+ }
+
+ private void CheckInput(final String type) throws ButtplugDeviceFeatureException {
+ if (input.get(type) == null) {
+ throw new ButtplugDeviceFeatureException(type);
+ }
+ }
+
+ public boolean HasBattery() {
+ return input.get("Battery") != null;
+ }
+
+ public Future ReadBattery() throws ButtplugDeviceFeatureException {
+ CheckInput("Battery");
+ return device.sendInputCommand(featureIndex, "Battery", InputCommandType.READ);
+ }
+
+ public boolean HasRssi() {
+ return input.get("Rssi") != null;
+ }
+
+ public Future ReadRssi() throws ButtplugDeviceFeatureException {
+ CheckInput("Rssi");
+ return device.sendInputCommand(featureIndex, "Rssi", InputCommandType.READ);
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ButtplugClientDeviceFeature that = (ButtplugClientDeviceFeature) o;
+ if (featureIndex != that.featureIndex ||
+ !description.equals(that.description) ||
+ (output == null) != (that.output == null) ||
+ (input == null) != (that.input == null)) {
+ return false;
+ }
+ if (output != null) {
+ if (output.size() != that.output.size() || output.keySet().containsAll(that.output.keySet())) {
+ return false;
+ }
+ for (String type : output.keySet()) {
+ if (!output.get(type).equals(that.output.get(type))) {
+ return false;
+ }
+ }
+ }
+ if (input != null) {
+ if (input.size() != that.input.size() || input.keySet().containsAll(that.input.keySet())) {
+ return false;
+ }
+ for (String type : input.keySet()) {
+ if (!input.get(type).equals(that.input.get(type))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/ButtplugDeviceFeatureException.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/ButtplugDeviceFeatureException.java
new file mode 100644
index 0000000..336b315
--- /dev/null
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/ButtplugDeviceFeatureException.java
@@ -0,0 +1,10 @@
+package io.github.blackspherefollower.buttplug4j.client;
+
+import io.github.blackspherefollower.buttplug4j.ButtplugException;
+
+public class ButtplugDeviceFeatureException extends ButtplugException {
+ public ButtplugDeviceFeatureException(final String cmd) {
+ super();
+ setMessage("Buttplug Device Feature does not support " + cmd);
+ }
+}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/IDeviceEvent.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/IDeviceAddedEvent.java
similarity index 61%
rename from buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/IDeviceEvent.java
rename to buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/IDeviceAddedEvent.java
index 2e77a45..a47ea7d 100644
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/IDeviceEvent.java
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/IDeviceAddedEvent.java
@@ -1,7 +1,5 @@
package io.github.blackspherefollower.buttplug4j.client;
-public interface IDeviceEvent {
+public interface IDeviceAddedEvent {
void deviceAdded(ButtplugClientDevice dev);
-
- void deviceRemoved(long index);
}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/IDeviceChangedEvent.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/IDeviceChangedEvent.java
new file mode 100644
index 0000000..afc3f4f
--- /dev/null
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/IDeviceChangedEvent.java
@@ -0,0 +1,5 @@
+package io.github.blackspherefollower.buttplug4j.client;
+
+public interface IDeviceChangedEvent {
+ void deviceChanged(ButtplugClientDevice dev);
+}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/IDeviceRemovedEvent.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/IDeviceRemovedEvent.java
new file mode 100644
index 0000000..3b1ded3
--- /dev/null
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/IDeviceRemovedEvent.java
@@ -0,0 +1,5 @@
+package io.github.blackspherefollower.buttplug4j.client;
+
+public interface IDeviceRemovedEvent {
+ void deviceRemoved(int index);
+}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/ISensorReadingEvent.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/ISensorReadingEvent.java
index b265722..628f650 100644
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/ISensorReadingEvent.java
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/client/ISensorReadingEvent.java
@@ -1,7 +1,7 @@
package io.github.blackspherefollower.buttplug4j.client;
-import io.github.blackspherefollower.buttplug4j.protocol.messages.SensorReading;
+import io.github.blackspherefollower.buttplug4j.protocol.messages.InputReading;
public interface ISensorReadingEvent {
- void sensorReadingReceived(SensorReading msg);
+ void sensorReadingReceived(InputReading msg);
}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/ButtplugConsts.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/ButtplugConsts.java
index b3b241b..a9b8663 100644
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/ButtplugConsts.java
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/ButtplugConsts.java
@@ -1,9 +1,10 @@
package io.github.blackspherefollower.buttplug4j.protocol;
public final class ButtplugConsts {
- public static final long SYSTEM_MSG_ID = 0;
- public static final long DEFAULT_MSG_ID = 1;
- public static final long MESSAGE_VERSION = 3;
+ public static final int SYSTEM_MSG_ID = 0;
+ public static final int DEFAULT_MSG_ID = 1;
+ public static final int PROTOCOL_VERSION_MAJOR = 4;
+ public static final int PROTOCOL_VERSION_MINOR = 0;
private ButtplugConsts() {
}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/ButtplugDeviceMessage.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/ButtplugDeviceMessage.java
index 908662b..886ebca 100644
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/ButtplugDeviceMessage.java
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/ButtplugDeviceMessage.java
@@ -7,7 +7,7 @@ public abstract class ButtplugDeviceMessage extends ButtplugMessage {
@JsonProperty(value = "DeviceIndex", required = true)
private long deviceIndex;
- public ButtplugDeviceMessage(final long id, final long deviceIndex) {
+ public ButtplugDeviceMessage(final int id, final long deviceIndex) {
super(id);
this.setDeviceIndex(deviceIndex);
}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/ButtplugJsonMessageParser.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/ButtplugJsonMessageParser.java
index d120ad1..36fe62f 100644
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/ButtplugJsonMessageParser.java
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/ButtplugJsonMessageParser.java
@@ -3,9 +3,11 @@
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.StreamReadFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTypeResolverBuilder;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
+import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
import java.util.Arrays;
@@ -16,7 +18,9 @@ public final class ButtplugJsonMessageParser {
private final ObjectMapper mapper;
public ButtplugJsonMessageParser() {
- mapper = new ObjectMapper();
+ mapper = JsonMapper.builder()
+ .enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION)
+ .build();
TypeResolverBuilder> typer = DefaultTypeResolverBuilder.construct(DefaultTyping.JAVA_LANG_OBJECT,
this.mapper.getPolymorphicTypeValidator());
typer = typer.init(JsonTypeInfo.Id.NAME, null);
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/ButtplugMessage.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/ButtplugMessage.java
index 445b9dd..80e25b5 100644
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/ButtplugMessage.java
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/ButtplugMessage.java
@@ -5,27 +5,21 @@
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
-import io.github.blackspherefollower.buttplug4j.protocol.messages.Error;
import io.github.blackspherefollower.buttplug4j.protocol.messages.*;
+import io.github.blackspherefollower.buttplug4j.protocol.messages.Error;
@JsonTypeInfo(include = As.WRAPPER_OBJECT, use = Id.NAME)
@JsonSubTypes({
- @JsonSubTypes.Type(value = DeviceAdded.class, name = "DeviceAdded"),
@JsonSubTypes.Type(value = DeviceList.class, name = "DeviceList"),
- @JsonSubTypes.Type(value = DeviceRemoved.class, name = "DeviceRemoved"),
@JsonSubTypes.Type(value = Error.class, name = "Error"),
- @JsonSubTypes.Type(value = LinearCmd.class, name = "LinearCmd"),
@JsonSubTypes.Type(value = Ok.class, name = "Ok"),
@JsonSubTypes.Type(value = Ping.class, name = "Ping"),
@JsonSubTypes.Type(value = RequestDeviceList.class, name = "RequestDeviceList"),
@JsonSubTypes.Type(value = RequestServerInfo.class, name = "RequestServerInfo"),
- @JsonSubTypes.Type(value = RotateCmd.class, name = "RotateCmd"),
- @JsonSubTypes.Type(value = ScalarCmd.class, name = "ScalarCmd"),
+ @JsonSubTypes.Type(value = OutputCmd.class, name = "OutputCmd"),
+ @JsonSubTypes.Type(value = InputCmd.class, name = "InputCmd"),
@JsonSubTypes.Type(value = ScanningFinished.class, name = "ScanningFinished"),
- @JsonSubTypes.Type(value = SensorReadCmd.class, name = "SensorReadCmd"),
- @JsonSubTypes.Type(value = SensorReading.class, name = "SensorReading"),
- @JsonSubTypes.Type(value = SensorSubscribeCmd.class, name = "SensorSubscribeCmd"),
- @JsonSubTypes.Type(value = SensorUnsubscribeCmd.class, name = "SensorUnsubscribeCmd"),
+ @JsonSubTypes.Type(value = InputReading.class, name = "InputReading"),
@JsonSubTypes.Type(value = ServerInfo.class, name = "ServerInfo"),
@JsonSubTypes.Type(value = StartScanning.class, name = "StartScanning"),
@JsonSubTypes.Type(value = StopAllDevices.class, name = "StopAllDevices"),
@@ -35,17 +29,17 @@
public abstract class ButtplugMessage {
@JsonProperty(value = "Id", required = true)
- private long id;
+ private int id;
- public ButtplugMessage(final long id) {
+ public ButtplugMessage(final int id) {
this.setId(id);
}
- public final long getId() {
+ public final int getId() {
return id;
}
- public final void setId(final long id) {
+ public final void setId(final int id) {
this.id = id;
}
}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/DeviceMessageInfo.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Device.java
similarity index 59%
rename from buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/DeviceMessageInfo.java
rename to buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Device.java
index e06d47c..7ba8ac4 100644
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/DeviceMessageInfo.java
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Device.java
@@ -1,16 +1,14 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages.Parts;
+package io.github.blackspherefollower.buttplug4j.protocol.messages;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import java.util.ArrayList;
+import java.util.HashMap;
-public final class DeviceMessageInfo {
+public class Device {
@JsonProperty(value = "DeviceIndex", required = true)
- private long deviceIndex;
+ private int deviceIndex;
@JsonProperty(value = "DeviceName", required = true)
private String deviceName;
@@ -22,35 +20,34 @@ public final class DeviceMessageInfo {
@JsonProperty(value = "DeviceDisplayName")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private String deviceDisplayName;
- @JsonProperty(value = "DeviceMessages", required = true)
- @JsonDeserialize(using = DeviceMessagesDeserializer.class)
- @JsonSerialize(using = DeviceMessagesSerializer.class)
- private ArrayList deviceMessages;
-
- public DeviceMessageInfo(final long deviceIndex, final String deviceName,
- final ArrayList deviceMessages, final int deviceMessageTimingGap,
- final String deviceDisplayName) {
+
+ @JsonProperty(value = "DeviceFeatures", required = true)
+ private HashMap deviceFeatures;
+
+ public Device(final int deviceIndex, final String deviceName,
+ final HashMap deviceFeatures, final int deviceMessageTimingGap,
+ final String deviceDisplayName) {
this.deviceName = deviceName;
this.deviceIndex = deviceIndex;
- this.deviceMessages = deviceMessages;
+ this.deviceFeatures = deviceFeatures;
this.deviceMessageTimingGap = deviceMessageTimingGap;
this.deviceDisplayName = deviceDisplayName;
}
@SuppressWarnings("unused")
- private DeviceMessageInfo() {
+ private Device() {
this.deviceName = "";
this.deviceIndex = -1;
- this.deviceMessages = new ArrayList<>();
+ this.deviceFeatures = new HashMap<>();
this.deviceMessageTimingGap = null;
this.deviceDisplayName = "";
}
- public long getDeviceIndex() {
+ public int getDeviceIndex() {
return deviceIndex;
}
- public void setDeviceIndex(final long deviceIndex) {
+ public void setDeviceIndex(final int deviceIndex) {
this.deviceIndex = deviceIndex;
}
@@ -78,11 +75,11 @@ public void setDeviceDisplayName(final String deviceDisplayName) {
this.deviceDisplayName = deviceDisplayName;
}
- public ArrayList getDeviceMessages() {
- return deviceMessages;
+ public HashMap getDeviceFeatures() {
+ return deviceFeatures;
}
- public void setDeviceMessages(final ArrayList deviceMessages) {
- this.deviceMessages = deviceMessages;
+ public void setDeviceFeatures(final HashMap deviceFeatures) {
+ this.deviceFeatures = deviceFeatures;
}
}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/DeviceAdded.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/DeviceAdded.java
deleted file mode 100644
index 4a13921..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/DeviceAdded.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import io.github.blackspherefollower.buttplug4j.protocol.ButtplugConsts;
-import io.github.blackspherefollower.buttplug4j.protocol.ButtplugDeviceMessage;
-import io.github.blackspherefollower.buttplug4j.protocol.messages.Parts.DeviceMessage;
-import io.github.blackspherefollower.buttplug4j.protocol.messages.Parts.DeviceMessagesDeserializer;
-import io.github.blackspherefollower.buttplug4j.protocol.messages.Parts.DeviceMessagesSerializer;
-
-import java.util.ArrayList;
-
-public final class DeviceAdded extends ButtplugDeviceMessage {
- @JsonProperty(value = "DeviceName", required = true)
- private String deviceName;
-
- @JsonProperty(value = "DeviceMessageTimingGap")
- @JsonInclude(JsonInclude.Include.NON_DEFAULT)
- private Integer deviceMessageTimingGap = null;
-
- @JsonProperty(value = "DeviceDisplayName")
- @JsonInclude(JsonInclude.Include.NON_EMPTY)
- private String deviceDisplayName;
-
- @JsonProperty(value = "DeviceMessages", required = true)
- @JsonDeserialize(using = DeviceMessagesDeserializer.class)
- @JsonSerialize(using = DeviceMessagesSerializer.class)
- private ArrayList deviceMessages;
-
- public DeviceAdded(final long deviceIndex, final String deviceName, final ArrayList deviceMessages) {
- super(ButtplugConsts.SYSTEM_MSG_ID, deviceIndex);
-
- this.setDeviceName(deviceName);
- this.setDeviceMessages(deviceMessages);
- }
-
- @SuppressWarnings("unused")
- private DeviceAdded() {
- super(ButtplugConsts.SYSTEM_MSG_ID, 0);
- this.setDeviceName("");
- this.setDeviceMessages(new ArrayList<>());
- }
-
- public String getDeviceName() {
- return deviceName;
- }
-
- public void setDeviceName(final String deviceName) {
- this.deviceName = deviceName;
- }
-
- public Integer getDeviceMessageTimingGap() {
- return deviceMessageTimingGap;
- }
-
- public void setDeviceMessageTimingGap(final Integer deviceMessageTimingGap) {
- this.deviceMessageTimingGap = deviceMessageTimingGap;
- }
-
- public String getDeviceDisplayName() {
- return deviceDisplayName;
- }
-
- public void setDeviceDisplayName(final String deviceDisplayName) {
- this.deviceDisplayName = deviceDisplayName;
- }
-
- public ArrayList getDeviceMessages() {
- return deviceMessages;
- }
-
- public void setDeviceMessages(final ArrayList deviceMessages) {
- this.deviceMessages = deviceMessages;
- }
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/DeviceFeature.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/DeviceFeature.java
new file mode 100644
index 0000000..7ee4176
--- /dev/null
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/DeviceFeature.java
@@ -0,0 +1,445 @@
+package io.github.blackspherefollower.buttplug4j.protocol.messages;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.TreeNode;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+public final class DeviceFeature {
+
+ @JsonProperty(value = "FeatureIndex", required = true)
+ private int featureIndex;
+ @JsonProperty(value = "FeatureDescription", required = true)
+ private String featureDescription;
+ @JsonProperty(value = "Output", required = false)
+ @JsonDeserialize(using = OutputDescriptorSetDeserialiser.class)
+ @JsonSerialize(using = OutputDescriptorSetSerialiser.class, include = JsonSerialize.Inclusion.NON_NULL)
+ private ArrayList output;
+ @JsonProperty(value = "Input", required = false)
+ @JsonDeserialize(using = InputDescriptorSetDeserialiser.class)
+ @JsonSerialize(using = InputDescriptorSetSerialiser.class, include = JsonSerialize.Inclusion.NON_NULL)
+ private ArrayList input;
+
+ public DeviceFeature() {
+ }
+
+ public String getFeatureDescription() {
+ return featureDescription;
+ }
+
+ public void setFeatureDescription(String featureDescription) {
+ this.featureDescription = featureDescription;
+ }
+
+ public int getFeatureIndex() {
+ return featureIndex;
+ }
+
+ public void setFeatureIndex(int featureIndex) {
+ this.featureIndex = featureIndex;
+ }
+
+ public ArrayList getOutput() {
+ return output;
+ }
+
+ public void setOutput(ArrayList output) {
+ this.output = output;
+ }
+
+ public ArrayList getInput() {
+ return input;
+ }
+
+ public void setInput(ArrayList input) {
+ this.input = input;
+ }
+
+ @JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME)
+ @JsonSubTypes({
+ @JsonSubTypes.Type(value = DeviceFeature.Vibrate.class, name = "Vibrate"),
+ @JsonSubTypes.Type(value = DeviceFeature.Rotate.class, name = "Rotate"),
+ @JsonSubTypes.Type(value = DeviceFeature.Spray.class, name = "Spray"),
+ @JsonSubTypes.Type(value = DeviceFeature.Oscillate.class, name = "Oscillate"),
+ @JsonSubTypes.Type(value = DeviceFeature.Position.class, name = "Position"),
+ @JsonSubTypes.Type(value = DeviceFeature.Temperature.class, name = "Temperature"),
+ @JsonSubTypes.Type(value = DeviceFeature.Constrict.class, name = "Constrict"),
+ @JsonSubTypes.Type(value = DeviceFeature.PositionWithDuration.class, name = "PositionWithDuration"),
+ @JsonSubTypes.Type(value = DeviceFeature.Led.class, name = "Led")
+ })
+ public interface OutputDescriptor {
+ }
+
+ public static class SteppedOutputDescriptor implements OutputDescriptor {
+ @JsonProperty(value = "Value", required = true)
+ private int[] value;
+
+ public SteppedOutputDescriptor(int[] value) {
+ this.value = value;
+ }
+
+ public int[] getValue() {
+ return value;
+ }
+
+ public void setStepCount(int[] value) {
+ this.value = value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ SteppedOutputDescriptor that = (SteppedOutputDescriptor) o;
+ return java.util.Arrays.equals(value, that.value);
+ }
+ }
+
+ public static class Vibrate extends SteppedOutputDescriptor {
+ public Vibrate(int[] value) {
+ super(value);
+ }
+
+ public Vibrate() {
+ super(new int[]{0, 0});
+ }
+ }
+
+ public static class Rotate extends SteppedOutputDescriptor {
+ public Rotate(int[] value) {
+ super(value);
+ }
+
+ public Rotate() {
+ super(new int[]{0, 0});
+ }
+ }
+
+ public static class Oscillate extends SteppedOutputDescriptor {
+ public Oscillate(int[] value) {
+ super(value);
+ }
+
+ public Oscillate() {
+ super(new int[]{0, 0});
+ }
+ }
+
+ public static class Constrict extends SteppedOutputDescriptor {
+ public Constrict(int[] value) {
+ super(value);
+ }
+
+ public Constrict() {
+ super(new int[]{0, 0});
+ }
+ }
+
+ public static class Spray extends SteppedOutputDescriptor {
+ public Spray(int[] value) {
+ super(value);
+ }
+
+ public Spray() {
+ super(new int[]{0, 0});
+ }
+ }
+
+ public static class Temperature extends SteppedOutputDescriptor {
+ public Temperature(int[] value) {
+ super(value);
+ }
+
+ public Temperature() {
+ super(new int[]{0, 0});
+ }
+ }
+
+ public static class Led extends SteppedOutputDescriptor {
+ public Led(int[] value) {
+ super(value);
+ }
+
+ public Led() {
+ super(new int[]{0, 0});
+ }
+ }
+
+ public static class Position extends SteppedOutputDescriptor {
+ public Position(int[] value) {
+ super(value);
+ }
+
+ public Position() {
+ super(new int[]{0, 0});
+ }
+ }
+
+ public static class PositionWithDuration implements OutputDescriptor {
+ @JsonProperty(value = "Position", required = true)
+ private int[] position;
+ @JsonProperty(value = "Duration", required = true)
+ private int[] duration;
+
+ public PositionWithDuration(int[] position, int[] duration) {
+ this.position = position;
+ this.duration = duration;
+ }
+
+ public PositionWithDuration() {
+ this.position = new int[]{0, 0};
+ this.duration = new int[]{0, 0};
+ }
+
+ public int[] getPosition() {
+ return position;
+ }
+
+ public void setPosition(int[] position) {
+ this.position = position;
+ }
+
+ public int[] getDuration() {
+ return duration;
+ }
+
+ public void setDuration(int[] duration) {
+ this.duration = duration;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ PositionWithDuration that = (PositionWithDuration) o;
+ return java.util.Arrays.equals(position, that.position) && java.util.Arrays.equals(duration, that.duration);
+ }
+ }
+
+ @JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME)
+ @JsonSubTypes({
+ @JsonSubTypes.Type(value = DeviceFeature.Battery.class, name = "Battery"),
+ @JsonSubTypes.Type(value = DeviceFeature.Rssi.class, name = "RSSI"),
+ @JsonSubTypes.Type(value = DeviceFeature.Button.class, name = "Button"),
+ @JsonSubTypes.Type(value = DeviceFeature.Pressure.class, name = "Pressure"),
+ @JsonSubTypes.Type(value = DeviceFeature.PositionInput.class, name = "Position")
+ })
+ public static class InputDescriptor {
+ @JsonProperty(value = "InputCommands", required = true)
+ private ArrayList input;
+
+ public InputDescriptor(ArrayList input) {
+ this.input = input;
+ }
+
+ public ArrayList getInput() {
+ return input;
+ }
+
+ public void setInput(ArrayList input) {
+ this.input = input;
+ }
+ }
+
+ public static class RangedInputDescriptor extends InputDescriptor {
+ @JsonProperty(value = "ValueRange", required = true)
+ private int[][] valueRange;
+
+ public RangedInputDescriptor(ArrayList input, int[][] valueRange) {
+ super(input);
+ this.valueRange = valueRange;
+ }
+
+ public RangedInputDescriptor() {
+ super(new ArrayList<>());
+ this.valueRange = new int[][]{{0, 0}, {0, 0}};
+ }
+
+ public int[][] getValueRange() {
+ return valueRange;
+ }
+
+ public void setValueRange(int[][] valueRange) {
+ this.valueRange = valueRange;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ RangedInputDescriptor that = (RangedInputDescriptor) o;
+ if (valueRange.length != that.valueRange.length) {
+ return false;
+ }
+ for (int i = 0; i < valueRange.length; i++) {
+ if (!java.util.Arrays.equals(valueRange[i], that.valueRange[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ public static class Battery extends RangedInputDescriptor {
+
+ public Battery(ArrayList input, int[][] valueRange) {
+ super(input, valueRange);
+ }
+
+ public Battery() {
+ super();
+ }
+ }
+
+ public static class Rssi extends RangedInputDescriptor {
+ public Rssi(ArrayList input, int[][] valueRange) {
+ super(input, valueRange);
+ }
+
+ public Rssi() {
+ super();
+ }
+ }
+
+ public static class Button extends RangedInputDescriptor {
+ public Button(ArrayList input, int[][] valueRange) {
+ super(input, valueRange);
+ }
+
+ public Button() {
+ super();
+ }
+ }
+
+ public static class Pressure extends RangedInputDescriptor {
+ public Pressure(ArrayList input, int[][] valueRange) {
+ super(input, valueRange);
+ }
+
+ public Pressure() {
+ super();
+ }
+ }
+
+ public static class PositionInput extends RangedInputDescriptor {
+ public PositionInput(ArrayList input, int[][] valueRange) {
+ super(input, valueRange);
+ }
+
+ public PositionInput() {
+ super();
+ }
+ }
+
+ static class OutputDescriptorSetDeserialiser extends JsonDeserializer> {
+
+ @Override
+ public ArrayList deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
+ ObjectMapper mapper = ((ObjectMapper) jsonParser.getCodec());
+ ArrayList ret = new ArrayList();
+ try {
+ TreeNode tree = jsonParser.readValueAsTree();
+ for (Iterator it = tree.fieldNames(); it.hasNext(); ) {
+ String f = it.next();
+ ObjectNode node = mapper.createObjectNode();
+ node.set(f, mapper.readTree(tree.get(f).traverse(mapper)));
+ ret.add(node.traverse(mapper).readValueAs(OutputDescriptor.class));
+ }
+ } catch (Exception e) {
+ System.out.println("Unknown OutputDescriptor: " + jsonParser.readValueAsTree());
+ // unknown type
+ }
+ return ret;
+ }
+ }
+
+ static class OutputDescriptorSetSerialiser extends JsonSerializer> {
+
+ @Override
+ public void serialize(ArrayList outputDescriptors, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
+
+ ObjectMapper mapper = ((ObjectMapper) jsonGenerator.getCodec());
+ ObjectNode node = null;
+ for (OutputDescriptor outputDescriptor : outputDescriptors) {
+ if (node == null) {
+ node = mapper.createObjectNode();
+ }
+
+ TreeNode n = mapper.readValue(mapper.writeValueAsString(outputDescriptor), JsonNode.class).traverse(mapper).readValueAsTree();
+ for (Iterator it = n.fieldNames(); it.hasNext(); ) {
+ String f = it.next();
+ ObjectNode on = mapper.createObjectNode();
+ node.set(f, mapper.readTree(n.get(f).traverse(mapper)));
+ }
+ }
+ jsonGenerator.writeObject(node);
+ }
+ }
+
+ static class InputDescriptorSetDeserialiser extends JsonDeserializer> {
+
+ @Override
+ public ArrayList deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
+ ObjectMapper mapper = ((ObjectMapper) jsonParser.getCodec());
+ ArrayList ret = new ArrayList();
+ try {
+ TreeNode tree = jsonParser.readValueAsTree();
+ for (Iterator it = tree.fieldNames(); it.hasNext(); ) {
+ String f = it.next();
+ ObjectNode node = mapper.createObjectNode();
+ node.set(f, mapper.readTree(tree.get(f).traverse(mapper)));
+ ret.add(node.traverse(mapper).readValueAs(InputDescriptor.class));
+ }
+ } catch (Exception e) {
+ System.out.println("Unknown InputDescriptor: " + jsonParser.readValueAsTree());
+ // unknown type
+ }
+ return ret;
+ }
+ }
+
+ static class InputDescriptorSetSerialiser extends JsonSerializer> {
+
+ @Override
+ public void serialize(ArrayList inputDescriptors, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
+
+ ObjectMapper mapper = ((ObjectMapper) jsonGenerator.getCodec());
+ ObjectNode node = null;
+ for (InputDescriptor inputDescriptor : inputDescriptors) {
+ if (node == null) {
+ node = mapper.createObjectNode();
+ }
+
+ TreeNode n = mapper.readValue(mapper.writeValueAsString(inputDescriptor), JsonNode.class).traverse(mapper).readValueAsTree();
+ for (Iterator it = n.fieldNames(); it.hasNext(); ) {
+ String f = it.next();
+ ObjectNode on = mapper.createObjectNode();
+ node.set(f, mapper.readTree(n.get(f).traverse(mapper)));
+ }
+ }
+ jsonGenerator.writeObject(node);
+ }
+ }
+}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/DeviceList.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/DeviceList.java
index b691020..542ec94 100644
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/DeviceList.java
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/DeviceList.java
@@ -3,31 +3,30 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import io.github.blackspherefollower.buttplug4j.protocol.ButtplugConsts;
import io.github.blackspherefollower.buttplug4j.protocol.ButtplugMessage;
-import io.github.blackspherefollower.buttplug4j.protocol.messages.Parts.DeviceMessageInfo;
-import java.util.ArrayList;
+import java.util.HashMap;
public final class DeviceList extends ButtplugMessage {
@JsonProperty(value = "Devices", required = true)
- private ArrayList devices;
+ private HashMap devices;
- public DeviceList(final ArrayList devices, final long id) {
+ public DeviceList(final HashMap devices, final int id) {
super(id);
- this.setDevices(devices);
+ this.devices = devices;
}
@SuppressWarnings("unused")
private DeviceList() {
super(ButtplugConsts.DEFAULT_MSG_ID);
- this.setDevices(new ArrayList<>());
+ this.setDevices(new HashMap<>());
}
- public ArrayList getDevices() {
+ public HashMap getDevices() {
return devices;
}
- public void setDevices(final ArrayList devices) {
+ public void setDevices(final HashMap devices) {
this.devices = devices;
}
}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/DeviceRemoved.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/DeviceRemoved.java
deleted file mode 100644
index 6b5bd10..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/DeviceRemoved.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages;
-
-import io.github.blackspherefollower.buttplug4j.protocol.ButtplugConsts;
-import io.github.blackspherefollower.buttplug4j.protocol.ButtplugDeviceMessage;
-
-public final class DeviceRemoved extends ButtplugDeviceMessage {
- public DeviceRemoved(final long deviceMessage) {
- super(ButtplugConsts.SYSTEM_MSG_ID, deviceMessage);
- }
-
- @SuppressWarnings("unused")
- private DeviceRemoved() {
- super(ButtplugConsts.SYSTEM_MSG_ID, -1);
- }
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Error.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Error.java
index 081979b..5b24e06 100644
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Error.java
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Error.java
@@ -16,7 +16,7 @@ public final class Error extends ButtplugMessage {
@JsonIgnore
private Throwable exception = null;
- public Error(final String errorMessage, final ErrorClass errorCode, final long id) {
+ public Error(final String errorMessage, final ErrorClass errorCode, final int id) {
super(id);
this.setErrorMessage(errorMessage);
this.setErrorCode(errorCode);
@@ -35,7 +35,8 @@ public Error(Throwable e) {
this.setErrorCode(ErrorClass.ERROR_UNKNOWN);
this.exception = e;
}
- public Error(Throwable e, final long id) {
+
+ public Error(Throwable e, final int id) {
super(id);
this.setErrorMessage(e.getMessage());
this.setErrorCode(ErrorClass.ERROR_UNKNOWN);
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/InputCmd.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/InputCmd.java
new file mode 100644
index 0000000..b6ffba8
--- /dev/null
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/InputCmd.java
@@ -0,0 +1,55 @@
+package io.github.blackspherefollower.buttplug4j.protocol.messages;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.github.blackspherefollower.buttplug4j.protocol.ButtplugDeviceMessage;
+
+public class InputCmd extends ButtplugDeviceMessage {
+
+ @JsonProperty(value = "FeatureIndex", required = true)
+ private int featureIndex;
+
+ @JsonProperty(value = "InputType", required = true)
+ private String inputType;
+
+ @JsonProperty(value = "InputCommand", required = true)
+ private InputCommandType inputCommand;
+
+ public InputCmd(int id, final long deviceIndex, final int featureIndex, final String inputType, final InputCommandType inputCommand) {
+ super(id, deviceIndex);
+ this.featureIndex = featureIndex;
+ this.inputType = inputType;
+ this.inputCommand = inputCommand;
+ }
+
+ public InputCmd() {
+ super(-1, -1);
+ this.featureIndex = -1;
+ this.inputType = "None";
+ this.inputCommand = InputCommandType.READ;
+ }
+
+
+ public int getFeatureIndex() {
+ return featureIndex;
+ }
+
+ public void setFeatureIndex(int featureIndex) {
+ this.featureIndex = featureIndex;
+ }
+
+ public String getInputType() {
+ return inputType;
+ }
+
+ public void setInputType(String inputType) {
+ this.inputType = inputType;
+ }
+
+ public InputCommandType getInputCommand() {
+ return inputCommand;
+ }
+
+ public void setInputCommand(InputCommandType inputCommand) {
+ this.inputCommand = inputCommand;
+ }
+}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/InputCommandType.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/InputCommandType.java
new file mode 100644
index 0000000..e702749
--- /dev/null
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/InputCommandType.java
@@ -0,0 +1,20 @@
+package io.github.blackspherefollower.buttplug4j.protocol.messages;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+
+public enum InputCommandType {
+ READ("Read"),
+ SUBSCRIBE("Subscribe"),
+ UNSUBSCRIBE("Unsubscribe");
+
+ private final String specName;
+
+ InputCommandType(String specName) {
+ this.specName = specName;
+ }
+
+ @JsonValue
+ public String getSpecName() {
+ return specName;
+ }
+}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/InputReading.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/InputReading.java
new file mode 100644
index 0000000..ad0846e
--- /dev/null
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/InputReading.java
@@ -0,0 +1,61 @@
+package io.github.blackspherefollower.buttplug4j.protocol.messages;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.github.blackspherefollower.buttplug4j.protocol.ButtplugDeviceMessage;
+
+public class InputReading extends ButtplugDeviceMessage {
+
+ @JsonProperty(value = "FeatureIndex", required = true)
+ private int featureIndex;
+ @JsonProperty(value = "Data", required = true)
+ private InputData data;
+
+ public InputReading(int id, long deviceIndex, int featureIndex) {
+ super(id, deviceIndex);
+ this.featureIndex = featureIndex;
+ }
+
+ public InputData getData() {
+ return data;
+ }
+
+ public void setData(InputData data) {
+ this.data = data;
+ }
+
+ public int getFeatureIndex() {
+ return featureIndex;
+ }
+
+ public void setFeatureIndex(int featureIndex) {
+ this.featureIndex = featureIndex;
+ }
+
+ public interface InputData {
+ }
+
+ static public class InputIntegerData {
+ @JsonProperty(value = "Data", required = true)
+ int value;
+
+ public int getValue() {
+ return value;
+ }
+
+ public void setValue(int value) {
+ this.value = value;
+ }
+ }
+
+ static public class BatteryData extends InputIntegerData {
+ }
+
+ static public class RssiData extends InputIntegerData {
+ }
+
+ static public class ButtonData extends InputIntegerData {
+ }
+
+ static public class PresureData extends InputIntegerData {
+ }
+}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/LinearCmd.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/LinearCmd.java
deleted file mode 100644
index 4bc8140..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/LinearCmd.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.github.blackspherefollower.buttplug4j.protocol.ButtplugConsts;
-import io.github.blackspherefollower.buttplug4j.protocol.ButtplugDeviceMessage;
-
-public final class LinearCmd extends ButtplugDeviceMessage {
-
- @JsonProperty(value = "Vectors", required = true)
- private LinearSubCmd[] vectors;
-
- public LinearCmd(final long deviceIndex, final LinearSubCmd[] vectors, final long id) {
- super(id, deviceIndex);
- this.setVectors(vectors);
- }
-
- @SuppressWarnings("unused")
- private LinearCmd() {
- super(ButtplugConsts.DEFAULT_MSG_ID, -1);
- }
-
- public LinearSubCmd[] getVectors() {
- return vectors;
- }
-
- public void setVectors(final LinearSubCmd[] vectors) {
- this.vectors = vectors;
- }
-
- public static final class LinearSubCmd {
- @JsonProperty(value = "Index", required = true)
- private long index;
-
- @JsonProperty(value = "Position", required = true)
- private double position;
-
- @JsonProperty(value = "Duration", required = true)
- private long duration;
-
- public LinearSubCmd(final long index, final double position, final long duration) {
- this.index = index;
- this.duration = duration;
- setPosition(position);
- }
-
- public LinearSubCmd() {
- this.index = -1;
- this.duration = 0;
- setPosition(0);
- }
-
- public double getPosition() {
- if (position > 1 || position < 0) {
- return 0;
- }
- return position;
- }
-
- public void setPosition(final double position) {
- if (position > 1) {
- throw new IllegalArgumentException(
- "Linear position cannot be greater than 1!");
- }
-
- if (position < 0) {
- throw new IllegalArgumentException(
- "Linear position cannot be lower than 0!");
- }
-
- this.position = position;
- }
-
- public long getDuration() {
- return duration;
- }
-
- public void setDuration(long duration) {
- this.duration = duration;
- }
-
- public long getIndex() {
- return index;
- }
-
- public void setIndex(long index) {
- this.index = index;
- }
- }
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Ok.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Ok.java
index 1b14231..015d237 100644
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Ok.java
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Ok.java
@@ -5,7 +5,7 @@
public final class Ok extends ButtplugMessage {
- public Ok(final long id) {
+ public Ok(final int id) {
super(id);
}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/OutputCmd.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/OutputCmd.java
new file mode 100644
index 0000000..10f1e00
--- /dev/null
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/OutputCmd.java
@@ -0,0 +1,187 @@
+package io.github.blackspherefollower.buttplug4j.protocol.messages;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import io.github.blackspherefollower.buttplug4j.protocol.ButtplugDeviceMessage;
+
+public class OutputCmd extends ButtplugDeviceMessage {
+
+
+ @JsonProperty(value = "FeatureIndex", required = true)
+ private long featureIndex;
+ @JsonProperty(value = "Command", required = true)
+ private IOutputCommand command;
+
+ public OutputCmd(int id, final long deviceIndex, final long featureIndex) {
+ super(id, deviceIndex);
+ this.featureIndex = featureIndex;
+ }
+
+ public OutputCmd() {
+ super(-1, -1);
+ this.featureIndex = -1;
+ }
+
+ public final long getFeatureIndex() {
+ return featureIndex;
+ }
+
+ public final void setFeatureIndex(final long deviceIndex) {
+ this.featureIndex = deviceIndex;
+ }
+
+ public final IOutputCommand getCommand() {
+ return command;
+ }
+
+ public final void setCommand(final IOutputCommand command) {
+ this.command = command;
+ }
+
+ @JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME)
+ @JsonSubTypes({
+ @JsonSubTypes.Type(value = Vibrate.class, name = "Vibrate"),
+ @JsonSubTypes.Type(value = Rotate.class, name = "Rotate"),
+ @JsonSubTypes.Type(value = Spray.class, name = "Spray"),
+ @JsonSubTypes.Type(value = Oscillate.class, name = "Oscillate"),
+ @JsonSubTypes.Type(value = Position.class, name = "Position"),
+ @JsonSubTypes.Type(value = Temperature.class, name = "Temperature"),
+ @JsonSubTypes.Type(value = Constrict.class, name = "Constrict"),
+ @JsonSubTypes.Type(value = PositionWithDuration.class, name = "PositionWithDuration"),
+ @JsonSubTypes.Type(value = Led.class, name = "Led")
+ })
+ public interface IOutputCommand {
+ }
+
+ public abstract static class ValueCommand implements IOutputCommand {
+ @JsonProperty(value = "Value", required = true)
+ private int value;
+
+ protected ValueCommand(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ public void setValue(int value) {
+ this.value = value;
+ }
+ }
+
+ public static class Vibrate extends ValueCommand {
+ public Vibrate(int value) {
+ super(value);
+ }
+
+ public Vibrate() {
+ super(0);
+ }
+ }
+
+ public static class Rotate extends ValueCommand {
+ public Rotate(int value) {
+ super(value);
+ }
+
+ public Rotate() {
+ super(0);
+ }
+ }
+
+ public static class Oscillate extends ValueCommand {
+ public Oscillate(int value) {
+ super(value);
+ }
+
+ public Oscillate() {
+ super(0);
+ }
+ }
+
+ public static class Constrict extends ValueCommand {
+ public Constrict(int value) {
+ super(value);
+ }
+
+ public Constrict() {
+ super(0);
+ }
+ }
+
+ public static class Spray extends ValueCommand {
+ public Spray(int value) {
+ super(value);
+ }
+
+ public Spray() {
+ super(0);
+ }
+ }
+
+ public static class Temperature extends ValueCommand {
+ public Temperature(int value) {
+ super(value);
+ }
+
+ public Temperature() {
+ super(0);
+ }
+ }
+
+ public static class Led extends ValueCommand {
+ public Led(int value) {
+ super(value);
+ }
+
+ public Led() {
+ super(0);
+ }
+ }
+
+ public static class Position extends ValueCommand {
+ public Position(int value) {
+ super(value);
+ }
+
+ public Position() {
+ super(0);
+ }
+ }
+
+ public static class PositionWithDuration implements IOutputCommand {
+ @JsonProperty(value = "Position", required = true)
+ private int position;
+ @JsonProperty(value = "Duration", required = true)
+ private int duration;
+
+ public PositionWithDuration(int position, int duration) {
+ this.position = position;
+ this.duration = duration;
+ }
+
+ public PositionWithDuration() {
+ this.position = 0;
+ this.duration = 0;
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ public void setPosition(int position) {
+ this.position = position;
+ }
+
+ public int getDuration() {
+ return duration;
+ }
+
+ public void setDuration(int duration) {
+ this.duration = duration;
+ }
+ }
+
+}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/DeviceMessage.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/DeviceMessage.java
deleted file mode 100644
index 8b80cbd..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/DeviceMessage.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages.Parts;
-
-public final class DeviceMessage {
- private String message;
- private MessageAttributes attributes;
-
- public String getMessage() {
- return message;
- }
-
- public void setMessage(final String message) {
- this.message = message;
- }
-
- public MessageAttributes getAttributes() {
- return attributes;
- }
-
- public void setAttributes(final MessageAttributes attributes) {
- this.attributes = attributes;
- }
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/DeviceMessagesDeserializer.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/DeviceMessagesDeserializer.java
deleted file mode 100644
index f7cb3eb..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/DeviceMessagesDeserializer.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages.Parts;
-
-import com.fasterxml.jackson.core.JsonParseException;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.ObjectCodec;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonDeserializer;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.Map;
-
-public final class DeviceMessagesDeserializer extends JsonDeserializer {
- @Override
- public Object deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException {
- ObjectCodec oc = p.getCodec();
- JsonNode node = oc.readTree(p);
-
- ArrayList ret = new ArrayList<>();
- for (Iterator> it = node.fields(); it.hasNext(); ) {
- Map.Entry msg = it.next();
- DeviceMessage dmsg = new DeviceMessage();
- dmsg.setMessage(msg.getKey());
-
- switch (dmsg.getMessage()) {
- case "StopDeviceCmd":
- dmsg.setAttributes(new NullMessageAttributes());
- break;
- case "RawReadCmd":
- case "RawWriteCmd":
- case "RawSubscribeCmd":
- dmsg.setAttributes(new ObjectMapper().treeToValue(msg.getValue(),
- RawMessageAttributes.class));
-
- break;
- case "SensorReadCmd":
- case "SensorSubscribeCmd":
- SensorMessageAttributes sattrs = new SensorMessageAttributes();
- for (Iterator it2 = msg.getValue().elements(); it2.hasNext(); ) {
- sattrs.getFeatures().add(new ObjectMapper().treeToValue(it2.next(),
- SensorFeatureAttributes.class));
- }
- dmsg.setAttributes(sattrs);
- break;
- case "ScalarCmd":
- case "LinearCmd":
- case "RotateCmd":
- GenericMessageAttributes gattrs = new GenericMessageAttributes();
- for (Iterator it2 = msg.getValue().elements(); it2.hasNext(); ) {
- gattrs.getFeatures().add(new ObjectMapper().treeToValue(it2.next(),
- GenericFeatureAttributes.class));
- }
- dmsg.setAttributes(gattrs);
- break;
- default:
- throw new JsonParseException(p, "Unknown Buttplug Device Message type: " + dmsg.getMessage());
- }
-
- ret.add(dmsg);
- }
- return ret;
- }
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/DeviceMessagesSerializer.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/DeviceMessagesSerializer.java
deleted file mode 100644
index b2d83b8..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/DeviceMessagesSerializer.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages.Parts;
-
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.SerializerProvider;
-
-import java.io.IOException;
-import java.util.List;
-
-public final class DeviceMessagesSerializer extends JsonSerializer {
- @Override
- public void serialize(final Object value, final JsonGenerator gen, final SerializerProvider serializers)
- throws IOException {
- gen.writeStartObject();
- if (value instanceof List>) {
- List> data = (List>) value;
- for (Object obj : data) {
- if (obj instanceof DeviceMessage) {
- DeviceMessage dmsg = (DeviceMessage) obj;
- gen.writeObjectField(dmsg.getMessage(), dmsg.getAttributes());
- }
- }
- }
- gen.writeEndObject();
- }
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/EmptySerializer.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/EmptySerializer.java
deleted file mode 100644
index 0a113bd..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/EmptySerializer.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages.Parts;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.SerializerProvider;
-
-import java.io.IOException;
-
-public final class EmptySerializer extends JsonSerializer {
-
- @Override
- public void serialize(final Object value, final JsonGenerator gen, final SerializerProvider serializers)
- throws IOException {
- gen.writeStartObject();
- gen.writeEndObject();
- }
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/GenericFeatureAttributes.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/GenericFeatureAttributes.java
deleted file mode 100644
index 5cd6612..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/GenericFeatureAttributes.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages.Parts;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-public final class GenericFeatureAttributes {
- @JsonProperty(value = "StepCount", required = true)
- private long stepCount;
-
- @JsonProperty(value = "FeatureDescriptor")
- @JsonInclude(JsonInclude.Include.NON_EMPTY)
- private String featureDescriptor;
-
- @JsonProperty(value = "ActuatorType")
- @JsonInclude(JsonInclude.Include.NON_EMPTY)
- private String actuatorType;
-
- public GenericFeatureAttributes() {
- }
-
- public long getStepCount() {
- return stepCount;
- }
-
- public void setStepCount(final long stepCount) {
- this.stepCount = stepCount;
- }
-
- public String getFeatureDescriptor() {
- return featureDescriptor;
- }
-
- public void setFeatureDescriptor(final String featureDescriptor) {
- this.featureDescriptor = featureDescriptor;
- }
-
- public String getActuatorType() {
- return actuatorType;
- }
-
- public void setActuatorType(final String actuatorType) {
- this.actuatorType = actuatorType;
- }
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/GenericFeaturesSerializer.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/GenericFeaturesSerializer.java
deleted file mode 100644
index 73d2ced..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/GenericFeaturesSerializer.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages.Parts;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.SerializerProvider;
-
-import java.io.IOException;
-
-public final class GenericFeaturesSerializer extends JsonSerializer {
- @Override
- public void serialize(final Object value, final JsonGenerator gen, final SerializerProvider serializers)
- throws IOException {
- GenericMessageAttributes data = (GenericMessageAttributes) value;
- gen.writeStartArray();
- for (GenericFeatureAttributes feat : data.getFeatures()) {
- gen.writeObject(feat);
- }
- gen.writeEndArray();
- }
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/GenericMessageAttributes.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/GenericMessageAttributes.java
deleted file mode 100644
index 2c1e27a..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/GenericMessageAttributes.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages.Parts;
-
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-
-import java.util.ArrayList;
-
-@JsonSerialize(using = GenericFeaturesSerializer.class)
-public final class GenericMessageAttributes extends MessageAttributes {
-
- private ArrayList features;
-
- public GenericMessageAttributes() {
- setFeatures(new ArrayList<>());
- }
-
- public ArrayList getFeatures() {
- return features;
- }
-
- public void setFeatures(final ArrayList features) {
- this.features = features;
- }
-}
-
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/MessageAttributes.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/MessageAttributes.java
deleted file mode 100644
index 671edab..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/MessageAttributes.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages.Parts;
-
-public abstract class MessageAttributes {
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/NullMessageAttributes.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/NullMessageAttributes.java
deleted file mode 100644
index 1eb5740..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/NullMessageAttributes.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages.Parts;
-
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-
-@JsonSerialize(using = EmptySerializer.class)
-public final class NullMessageAttributes extends MessageAttributes {
-
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/RawMessageAttributes.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/RawMessageAttributes.java
deleted file mode 100644
index 2636e0b..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/RawMessageAttributes.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages.Parts;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-public final class RawMessageAttributes extends MessageAttributes {
-
- @JsonProperty(value = "Endpoints", required = true)
- private String[] endpoints;
-
- public String[] getEndpoints() {
- return endpoints;
- }
-
- public void setEndpoints(final String[] endpoints) {
- this.endpoints = endpoints;
- }
-}
-
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/SensorFeatureAttributes.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/SensorFeatureAttributes.java
deleted file mode 100644
index 86c9b18..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/SensorFeatureAttributes.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages.Parts;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-public final class SensorFeatureAttributes {
- @JsonProperty(value = "SensorType", required = true)
- private String sensorType;
-
- @JsonProperty(value = "FeatureDescriptor")
- private String featureDescriptor;
-
- @JsonProperty(value = "SensorRange", required = true)
- private int[][] sensorRange;
-
- public String getSensorType() {
- return sensorType;
- }
-
- public void setSensorType(final String sensorType) {
- this.sensorType = sensorType;
- }
-
- public String getFeatureDescriptor() {
- return featureDescriptor;
- }
-
- public void setFeatureDescriptor(final String featureDescriptor) {
- this.featureDescriptor = featureDescriptor;
- }
-
- public int[][] getSensorRange() {
- return sensorRange;
- }
-
- public void setSensorRange(final int[][] sensorRange) {
- this.sensorRange = sensorRange;
- }
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/SensorFeaturesSerializer.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/SensorFeaturesSerializer.java
deleted file mode 100644
index e019c8c..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/SensorFeaturesSerializer.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages.Parts;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.JsonSerializer;
-import com.fasterxml.jackson.databind.SerializerProvider;
-
-import java.io.IOException;
-
-public final class SensorFeaturesSerializer extends JsonSerializer {
- @Override
- public void serialize(final Object value, final JsonGenerator gen, final SerializerProvider serializers)
- throws IOException {
- SensorMessageAttributes data = (SensorMessageAttributes) value;
- gen.writeStartArray();
- for (SensorFeatureAttributes feat : data.getFeatures()) {
- gen.writeObject(feat);
- }
- gen.writeEndArray();
- }
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/SensorMessageAttributes.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/SensorMessageAttributes.java
deleted file mode 100644
index d9e42d1..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/SensorMessageAttributes.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages.Parts;
-
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-
-import java.util.ArrayList;
-
-@JsonSerialize(using = SensorFeaturesSerializer.class)
-public final class SensorMessageAttributes extends MessageAttributes {
-
- private ArrayList features;
-
- SensorMessageAttributes() {
- setFeatures(new ArrayList<>());
- }
-
- public ArrayList getFeatures() {
- return features;
- }
-
- public void setFeatures(final ArrayList features) {
- this.features = features;
- }
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/package-info.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/package-info.java
deleted file mode 100644
index c2cb28f..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Parts/package-info.java
+++ /dev/null
@@ -1 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages.Parts;
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Ping.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Ping.java
index b452cec..ee5bbf0 100644
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Ping.java
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/Ping.java
@@ -10,7 +10,7 @@ private Ping() {
super(ButtplugConsts.DEFAULT_MSG_ID);
}
- public Ping(final long id) {
+ public Ping(final int id) {
super(id);
}
}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/RequestDeviceList.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/RequestDeviceList.java
index f0e9c35..b49cc49 100644
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/RequestDeviceList.java
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/RequestDeviceList.java
@@ -10,7 +10,7 @@ private RequestDeviceList() {
super(ButtplugConsts.DEFAULT_MSG_ID);
}
- public RequestDeviceList(final long id) {
+ public RequestDeviceList(final int id) {
super(id);
}
}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/RequestServerInfo.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/RequestServerInfo.java
index d93e883..f158f26 100644
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/RequestServerInfo.java
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/RequestServerInfo.java
@@ -4,16 +4,20 @@
import io.github.blackspherefollower.buttplug4j.protocol.ButtplugConsts;
import io.github.blackspherefollower.buttplug4j.protocol.ButtplugMessage;
-import static io.github.blackspherefollower.buttplug4j.protocol.ButtplugConsts.MESSAGE_VERSION;
+import static io.github.blackspherefollower.buttplug4j.protocol.ButtplugConsts.PROTOCOL_VERSION_MAJOR;
+import static io.github.blackspherefollower.buttplug4j.protocol.ButtplugConsts.PROTOCOL_VERSION_MINOR;
public final class RequestServerInfo extends ButtplugMessage {
- @JsonProperty(value = "MessageVersion", required = true)
- private final long messageVersion = MESSAGE_VERSION;
+ @JsonProperty(value = "ProtocolVersionMajor", required = true)
+ private final long protocolVersionMajor = PROTOCOL_VERSION_MAJOR;
+
+ @JsonProperty(value = "ProtocolVersionMinor", required = true)
+ private final long protocolVersionMinor = PROTOCOL_VERSION_MINOR;
@JsonProperty(value = "ClientName", required = true)
private String clientName;
- public RequestServerInfo(final String clientName, final long id) {
+ public RequestServerInfo(final String clientName, final int id) {
super(id);
this.setClientName(clientName);
}
@@ -24,8 +28,12 @@ private RequestServerInfo() {
this.setClientName("");
}
- public long getMessageVersion() {
- return messageVersion;
+ public long getProtocolVersionMajor() {
+ return protocolVersionMajor;
+ }
+
+ public long getProtocolVersionMinor() {
+ return protocolVersionMinor;
}
public String getClientName() {
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/RotateCmd.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/RotateCmd.java
deleted file mode 100644
index 36dfeaf..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/RotateCmd.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.github.blackspherefollower.buttplug4j.protocol.ButtplugConsts;
-import io.github.blackspherefollower.buttplug4j.protocol.ButtplugDeviceMessage;
-
-public final class RotateCmd extends ButtplugDeviceMessage {
-
- @JsonProperty(value = "Rotations", required = true)
- private RotateSubCmd[] rotations;
-
- public RotateCmd(final long deviceIndex, final RotateSubCmd[] rotations, final long id) {
- super(id, deviceIndex);
- this.setRotations(rotations);
- }
-
- @SuppressWarnings("unused")
- private RotateCmd() {
- super(ButtplugConsts.DEFAULT_MSG_ID, -1);
- }
-
- public RotateSubCmd[] getRotations() {
- return rotations;
- }
-
- public void setRotations(final RotateSubCmd[] rotations) {
- this.rotations = rotations;
- }
-
- public static final class RotateSubCmd {
- @JsonProperty(value = "Index", required = true)
- private long index;
-
- @JsonProperty(value = "Speed", required = true)
- private double speed;
-
- public boolean isClockwise() {
- return clockwise;
- }
-
- public void setClockwise(boolean clockwise) {
- this.clockwise = clockwise;
- }
-
- @JsonProperty(value = "Clockwise", required = true)
- private boolean clockwise;
-
- public RotateSubCmd(final long index, final double speed, final boolean clockwise) {
- this.index = index;
- this.clockwise = clockwise;
- setSpeed(speed);
- }
-
- public RotateSubCmd() {
- this.index = -1;
- this.clockwise = true;
- this.speed = 0;
- }
-
- public double getSpeed() {
- if (speed > 1 || speed < 0) {
- return 0;
- }
- return speed;
- }
-
- public void setSpeed(final double speed) {
- if (speed > 1) {
- throw new IllegalArgumentException(
- "Rotation speed cannot be greater than 1!");
- }
-
- if (speed < 0) {
- throw new IllegalArgumentException(
- "Rotation speed cannot be lower than 0!");
- }
-
- this.speed = speed;
- }
-
- public long getIndex() {
- return index;
- }
-
- public void setIndex(long index) {
- this.index = index;
- }
- }
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/ScalarCmd.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/ScalarCmd.java
deleted file mode 100644
index 550b236..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/ScalarCmd.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.github.blackspherefollower.buttplug4j.protocol.ButtplugConsts;
-import io.github.blackspherefollower.buttplug4j.protocol.ButtplugDeviceMessage;
-import io.github.blackspherefollower.buttplug4j.util.Pair;
-
-import java.util.ArrayList;
-
-public final class ScalarCmd extends ButtplugDeviceMessage {
-
- @JsonProperty(value = "Scalars", required = true)
- private ScalarSubCmd[] scalars;
-
- public ScalarCmd(final long deviceIndex, final Pair[] scalars, final long id) {
- super(id, deviceIndex);
- long i = 0;
- ArrayList scalarsubs = new ArrayList<>();
- for (Pair scalar : scalars) {
- if (scalar != null) {
- scalarsubs.add(new ScalarSubCmd(i, scalar.getLeft(), scalar.getRight()));
- }
- i++;
- }
- this.setScalars(scalarsubs.toArray(new ScalarSubCmd[]{}));
- }
-
- @SuppressWarnings("unused")
- private ScalarCmd() {
- super(ButtplugConsts.DEFAULT_MSG_ID, -1);
- }
-
- public ScalarSubCmd[] getScalars() {
- return scalars;
- }
-
- public void setScalars(final ScalarSubCmd[] scalars) {
- this.scalars = scalars;
- }
-
- public static final class ScalarSubCmd {
- @JsonProperty(value = "Index", required = true)
- private long index;
-
- @JsonProperty(value = "Scalar", required = true)
- private double scalar;
-
- @JsonProperty(value = "ActuatorType", required = true)
- private String actuatorType;
-
- public ScalarSubCmd(final long index, final double scalar, final String actuatorType) {
- this.setIndex(index);
- this.setActuatorType(actuatorType);
- setScalar(scalar);
- }
-
- public ScalarSubCmd() {
- this.index = -1;
- this.actuatorType = "";
- this.scalar = 0;
- }
-
- public double getScalar() {
- if (scalar > 1 || scalar < 0) {
- return 0;
- }
- return scalar;
- }
-
- public void setScalar(final double scalar) {
- if (scalar > 1) {
- throw new IllegalArgumentException(
- "Scalar values cannot be greater than 1!");
- }
-
- if (scalar < 0) {
- throw new IllegalArgumentException(
- "Scalar values cannot be lower than 0!");
- }
-
- this.scalar = scalar;
- }
-
- public long getIndex() {
- return index;
- }
-
- public void setIndex(final long index) {
- this.index = index;
- }
-
- public String getActuatorType() {
- return actuatorType;
- }
-
- public void setActuatorType(final String actuatorType) {
- this.actuatorType = actuatorType;
- }
- }
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/SensorReadCmd.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/SensorReadCmd.java
deleted file mode 100644
index b168d03..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/SensorReadCmd.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.github.blackspherefollower.buttplug4j.protocol.ButtplugConsts;
-import io.github.blackspherefollower.buttplug4j.protocol.ButtplugDeviceMessage;
-
-public final class SensorReadCmd extends ButtplugDeviceMessage {
-
- @JsonProperty(value = "SensorIndex", required = true)
- private int sensorIndex;
- @JsonProperty(value = "SensorType", required = true)
- private String sensorType;
-
- public SensorReadCmd(final long deviceIndex, final long id) {
- super(id, deviceIndex);
- }
-
- @SuppressWarnings("unused")
- private SensorReadCmd() {
- super(ButtplugConsts.DEFAULT_MSG_ID, -1);
- }
-
- public int getSensorIndex() {
- return sensorIndex;
- }
-
- public void setSensorIndex(final int sensorIndex) {
- this.sensorIndex = sensorIndex;
- }
-
- public String getSensorType() {
- return sensorType;
- }
-
- public void setSensorType(final String sensorType) {
- this.sensorType = sensorType;
- }
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/SensorReading.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/SensorReading.java
deleted file mode 100644
index 0afa901..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/SensorReading.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.github.blackspherefollower.buttplug4j.protocol.ButtplugConsts;
-import io.github.blackspherefollower.buttplug4j.protocol.ButtplugDeviceMessage;
-
-public final class SensorReading extends ButtplugDeviceMessage {
-
- @JsonProperty(value = "SensorIndex", required = true)
- private int sensorIndex;
-
- @JsonProperty(value = "SensorType", required = true)
- private String sensorType;
-
- @JsonProperty(value = "Data", required = true)
- private byte[] data;
-
- public SensorReading(final long deviceIndex, final long id) {
- super(id, deviceIndex);
- }
-
- @SuppressWarnings("unused")
- private SensorReading() {
- super(ButtplugConsts.DEFAULT_MSG_ID, -1);
- }
-
- public int getSensorIndex() {
- return sensorIndex;
- }
-
- public void setSensorIndex(final int sensorIndex) {
- this.sensorIndex = sensorIndex;
- }
-
- public String getSensorType() {
- return sensorType;
- }
-
- public void setSensorType(final String sensorType) {
- this.sensorType = sensorType;
- }
-
- public byte[] getData() {
- return data;
- }
-
- public void setData(final byte[] data) {
- this.data = data;
- }
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/SensorSubscribeCmd.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/SensorSubscribeCmd.java
deleted file mode 100644
index 0ad27c7..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/SensorSubscribeCmd.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.github.blackspherefollower.buttplug4j.protocol.ButtplugConsts;
-import io.github.blackspherefollower.buttplug4j.protocol.ButtplugDeviceMessage;
-
-public final class SensorSubscribeCmd extends ButtplugDeviceMessage {
-
- @JsonProperty(value = "SensorIndex", required = true)
- private int sensorIndex;
- @JsonProperty(value = "SensorType", required = true)
- private String sensorType;
-
- public SensorSubscribeCmd(final long deviceIndex, final long id) {
- super(id, deviceIndex);
- }
-
- @SuppressWarnings("unused")
- private SensorSubscribeCmd() {
- super(ButtplugConsts.DEFAULT_MSG_ID, -1);
- }
-
- public int getSensorIndex() {
- return sensorIndex;
- }
-
- public void setSensorIndex(final int sensorIndex) {
- this.sensorIndex = sensorIndex;
- }
-
- public String getSensorType() {
- return sensorType;
- }
-
- public void setSensorType(final String sensorType) {
- this.sensorType = sensorType;
- }
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/SensorUnsubscribeCmd.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/SensorUnsubscribeCmd.java
deleted file mode 100644
index 012baf6..0000000
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/SensorUnsubscribeCmd.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package io.github.blackspherefollower.buttplug4j.protocol.messages;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import io.github.blackspherefollower.buttplug4j.protocol.ButtplugConsts;
-import io.github.blackspherefollower.buttplug4j.protocol.ButtplugDeviceMessage;
-
-public final class SensorUnsubscribeCmd extends ButtplugDeviceMessage {
-
- @JsonProperty(value = "SensorIndex", required = true)
- private int sensorIndex;
- @JsonProperty(value = "SensorType", required = true)
- private String sensorType;
-
- public SensorUnsubscribeCmd(final long deviceIndex, final long id) {
- super(id, deviceIndex);
- }
-
- @SuppressWarnings("unused")
- private SensorUnsubscribeCmd() {
- super(ButtplugConsts.DEFAULT_MSG_ID, -1);
- }
-
- public int getSensorIndex() {
- return sensorIndex;
- }
-
- public void setSensorIndex(final int sensorIndex) {
- this.sensorIndex = sensorIndex;
- }
-
- public String getSensorType() {
- return sensorType;
- }
-
- public void setSensorType(final String sensorType) {
- this.sensorType = sensorType;
- }
-}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/ServerInfo.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/ServerInfo.java
index 8c2db68..6e6de43 100644
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/ServerInfo.java
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/ServerInfo.java
@@ -6,8 +6,11 @@
public final class ServerInfo extends ButtplugMessage {
- @JsonProperty(value = "MessageVersion", required = true)
- private int messageVersion;
+ @JsonProperty(value = "ProtocolVersionMajor", required = true)
+ private int protocolVersionMajor;
+
+ @JsonProperty(value = "ProtocolVersionMinor", required = true)
+ private int protocolVersionMinor;
@JsonProperty(value = "MaxPingTime", required = true)
private long maxPingTime;
@@ -15,29 +18,39 @@ public final class ServerInfo extends ButtplugMessage {
@JsonProperty(value = "ServerName", required = true)
private String serverName;
- public ServerInfo(final String serverName, final int messageVersion, final long maxPingTime, final long id) {
+ public ServerInfo(final String serverName, final int protocolVersionMajor, final int protocolVersionMinor, final long maxPingTime, final int id) {
super(id);
- this.setServerName(serverName);
- this.setMessageVersion(messageVersion);
- this.setMaxPingTime(maxPingTime);
+ this.serverName = serverName;
+ this.protocolVersionMajor = protocolVersionMajor;
+ this.protocolVersionMinor = protocolVersionMinor;
+ this.maxPingTime = maxPingTime;
}
@SuppressWarnings("unused")
private ServerInfo() {
super(ButtplugConsts.DEFAULT_MSG_ID);
- this.setServerName("");
- this.setMessageVersion(1);
- this.setMaxPingTime(0);
+ this.serverName = "";
+ this.protocolVersionMajor = 4;
+ this.protocolVersionMinor = 0;
+ this.maxPingTime = 0;
+ }
+
+ public int getProtocolVersionMajor() {
+ return protocolVersionMajor;
+ }
+
+ public void setProtocolVersionMajor(final int protocolVersionMajor) {
+ this.protocolVersionMajor = protocolVersionMajor;
}
- public int getMessageVersion() {
- return messageVersion;
+ public int getProtocolVersionMinor() {
+ return protocolVersionMinor;
}
- public void setMessageVersion(final int messageVersion) {
- this.messageVersion = messageVersion;
+ public void setProtocolVersionMinor(final int protocolVersionMinor) {
+ this.protocolVersionMinor = protocolVersionMinor;
}
public long getMaxPingTime() {
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/StartScanning.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/StartScanning.java
index b31c679..2898b20 100644
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/StartScanning.java
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/StartScanning.java
@@ -10,7 +10,7 @@ private StartScanning() {
super(ButtplugConsts.DEFAULT_MSG_ID);
}
- public StartScanning(final long id) {
+ public StartScanning(final int id) {
super(id);
}
}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/StopAllDevices.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/StopAllDevices.java
index 1168448..edb6f65 100644
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/StopAllDevices.java
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/StopAllDevices.java
@@ -10,7 +10,7 @@ private StopAllDevices() {
super(ButtplugConsts.DEFAULT_MSG_ID);
}
- public StopAllDevices(final long id) {
+ public StopAllDevices(final int id) {
super(id);
}
}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/StopDeviceCmd.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/StopDeviceCmd.java
index 8487d3b..b2ec26e 100644
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/StopDeviceCmd.java
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/StopDeviceCmd.java
@@ -5,7 +5,7 @@
public final class StopDeviceCmd extends ButtplugDeviceMessage {
- public StopDeviceCmd(final long deviceIndex, final long id) {
+ public StopDeviceCmd(final int id, final long deviceIndex) {
super(id, deviceIndex);
}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/StopScanning.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/StopScanning.java
index 6bcf30d..35dd4bd 100644
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/StopScanning.java
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/protocol/messages/StopScanning.java
@@ -10,7 +10,7 @@ private StopScanning() {
super(ButtplugConsts.DEFAULT_MSG_ID);
}
- public StopScanning(final long id) {
+ public StopScanning(final int id) {
super(id);
}
}
diff --git a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/util/Pair.java b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/util/Pair.java
index 31355e8..c2e3b8d 100644
--- a/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/util/Pair.java
+++ b/buttplug4j/src/main/java/io/github/blackspherefollower/buttplug4j/util/Pair.java
@@ -27,4 +27,25 @@ public B setRight(final B value) {
b = value;
return b;
}
+
+ @Override
+ public String toString() {
+ return String.format("(%s, %s)", a, b);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ return ((Pair, ?>) o).a.equals(a) && ((Pair, ?>) o).b.equals(b);
+ }
+
+ @Override
+ public int hashCode() {
+ return a.hashCode() ^ b.hashCode();
+ }
}
diff --git a/buttplug4j/src/test/java/io/github/blackspherefollower/buttplug4j/ButtplugExceptionTest.java b/buttplug4j/src/test/java/io/github/blackspherefollower/buttplug4j/ButtplugExceptionTest.java
new file mode 100644
index 0000000..fac4ec2
--- /dev/null
+++ b/buttplug4j/src/test/java/io/github/blackspherefollower/buttplug4j/ButtplugExceptionTest.java
@@ -0,0 +1,17 @@
+
+package io.github.blackspherefollower.buttplug4j;
+
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class ButtplugExceptionTest {
+
+ @Test
+ public void testExceptionWithMessage() {
+ String errorMessage = "Test error message";
+ ButtplugException exception = new ButtplugException();
+ exception.setMessage(errorMessage);
+
+ assertEquals(errorMessage, exception.getMessage());
+ }
+}
diff --git a/buttplug4j/src/test/java/io/github/blackspherefollower/buttplug4j/client/ButtplugClientDeviceFeatureTest.java b/buttplug4j/src/test/java/io/github/blackspherefollower/buttplug4j/client/ButtplugClientDeviceFeatureTest.java
new file mode 100644
index 0000000..6625a2a
--- /dev/null
+++ b/buttplug4j/src/test/java/io/github/blackspherefollower/buttplug4j/client/ButtplugClientDeviceFeatureTest.java
@@ -0,0 +1,367 @@
+
+package io.github.blackspherefollower.buttplug4j.client;
+
+import io.github.blackspherefollower.buttplug4j.protocol.ButtplugMessage;
+import io.github.blackspherefollower.buttplug4j.protocol.messages.DeviceFeature;
+import io.github.blackspherefollower.buttplug4j.protocol.messages.OutputCmd;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.util.ArrayList;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+class ButtplugClientDeviceFeatureTest {
+
+ private ButtplugClientDevice mockDevice;
+ private DeviceFeature testFeature;
+ private ButtplugClientDeviceFeature clientFeature;
+
+ @BeforeEach
+ void setup() {
+ mockDevice = mock(ButtplugClientDevice.class);
+
+ // Create a test feature with various output types
+ testFeature = new DeviceFeature();
+ testFeature.setFeatureIndex(0);
+ testFeature.setFeatureDescription("Test Feature");
+
+ ArrayList outputs = new ArrayList<>();
+ outputs.add(new DeviceFeature.Vibrate(new int[]{0, 100}));
+ outputs.add(new DeviceFeature.Rotate(new int[]{0, 50}));
+ outputs.add(new DeviceFeature.Oscillate(new int[]{0, 20}));
+ outputs.add(new DeviceFeature.Constrict(new int[]{0, 10}));
+ outputs.add(new DeviceFeature.Spray(new int[]{0, 5}));
+ outputs.add(new DeviceFeature.Position(new int[]{0, 25}));
+ outputs.add(new DeviceFeature.Led(new int[]{0, 255}));
+ outputs.add(new DeviceFeature.Temperature(new int[]{0, 30}));
+ testFeature.setOutput(outputs);
+
+ ArrayList inputs = new ArrayList<>();
+ testFeature.setInput(inputs);
+
+ clientFeature = new ButtplugClientDeviceFeature(mockDevice, testFeature);
+ }
+
+ @Test
+ void testConstructor() {
+ assertEquals("Test Feature", clientFeature.getDescription());
+ }
+
+ @Test
+ void testVibrateWithValidStep() throws Exception {
+ CompletableFuture future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+
+ Future result = clientFeature.Vibrate(50);
+
+ assertNotNull(result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertInstanceOf(OutputCmd.Vibrate.class, captor.getValue());
+ assertEquals(50, ((OutputCmd.Vibrate) captor.getValue()).getValue());
+ }
+
+ @Test
+ void testVibrateWithInvalidStepThrowsException() {
+ assertThrows(ButtplugDeviceFeatureException.class, () -> clientFeature.Vibrate(150));
+ assertThrows(ButtplugDeviceFeatureException.class, () -> clientFeature.Vibrate(-1));
+ }
+
+ @Test
+ void testVibrateFloatWithValidValue() throws Exception {
+ CompletableFuture future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+
+ Future result = clientFeature.VibrateFloat(0.5f);
+
+ assertNotNull(result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertInstanceOf(OutputCmd.Vibrate.class, captor.getValue());
+ assertEquals(50, ((OutputCmd.Vibrate) captor.getValue()).getValue());
+ }
+
+ @Test
+ void testVibrateFloatWithBoundaryValues() throws Exception {
+ CompletableFuture future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+
+ // Test minimum value
+ clientFeature.VibrateFloat(0.0f);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertEquals(0, ((OutputCmd.Vibrate) captor.getValue()).getValue());
+
+ // Test maximum value
+ reset(mockDevice);
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+ clientFeature.VibrateFloat(1.0f);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertEquals(100, ((OutputCmd.Vibrate) captor.getValue()).getValue());
+ }
+
+ @Test
+ void testVibrateFloatWithInvalidValueThrowsException() {
+ assertThrows(ButtplugDeviceFeatureException.class, () -> clientFeature.VibrateFloat(1.5f));
+ assertThrows(ButtplugDeviceFeatureException.class, () -> clientFeature.VibrateFloat(-0.1f));
+ }
+
+ @Test
+ void testRotateWithValidStep() throws Exception {
+ CompletableFuture future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+
+ Future result = clientFeature.Rotate(25);
+
+ assertNotNull(result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertInstanceOf(OutputCmd.Rotate.class, captor.getValue());
+ assertEquals(25, ((OutputCmd.Rotate) captor.getValue()).getValue());
+ }
+
+ @Test
+ void testRotateFloatWithValidValue() throws Exception {
+ CompletableFuture future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+
+ Future result = clientFeature.RotateFloat(0.5f);
+
+ assertNotNull(result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertInstanceOf(OutputCmd.Rotate.class, captor.getValue());
+ assertEquals(25, ((OutputCmd.Rotate) captor.getValue()).getValue());
+ }
+
+ @Test
+ void testConstrictWithValidStep() throws Exception {
+ CompletableFuture future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+
+ Future result = clientFeature.Constrict(5);
+
+ assertNotNull(result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertInstanceOf(OutputCmd.Constrict.class, captor.getValue());
+ assertEquals(5, ((OutputCmd.Constrict) captor.getValue()).getValue());
+ }
+
+ @Test
+ void testConstrictFloatWithValidValue() throws Exception {
+ CompletableFuture future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+
+ Future result = clientFeature.ConstrictFloat(0.5f);
+
+ assertNotNull(result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertInstanceOf(OutputCmd.Constrict.class, captor.getValue());
+ assertEquals(5, ((OutputCmd.Constrict) captor.getValue()).getValue());
+ }
+
+ @Test
+ void testSprayWithValidStep() throws Exception {
+ CompletableFuture future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+
+ Future result = clientFeature.Spray(3);
+
+ assertNotNull(result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertInstanceOf(OutputCmd.Spray.class, captor.getValue());
+ assertEquals(3, ((OutputCmd.Spray) captor.getValue()).getValue());
+ }
+
+ @Test
+ void testSprayFloatWithValidValue() throws Exception {
+ CompletableFuture future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+
+ Future result = clientFeature.SprayFloat(0.6f);
+
+ assertNotNull(result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertInstanceOf(OutputCmd.Spray.class, captor.getValue());
+ assertEquals(3, ((OutputCmd.Spray) captor.getValue()).getValue());
+ }
+
+ @Test
+ void testPositionWithValidStep() throws Exception {
+ CompletableFuture future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+
+ Future result = clientFeature.Position(15);
+
+ assertNotNull(result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertInstanceOf(OutputCmd.Position.class, captor.getValue());
+ assertEquals(15, ((OutputCmd.Position) captor.getValue()).getValue());
+ }
+
+ @Test
+ void testPositionFloatWithValidValue() throws Exception {
+ CompletableFuture future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+
+ Future result = clientFeature.PositionFloat(0.8f);
+
+ assertNotNull(result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertInstanceOf(OutputCmd.Position.class, captor.getValue());
+ assertEquals(20, ((OutputCmd.Position) captor.getValue()).getValue());
+ }
+
+ @Test
+ void testPositionWithDurationWithValidStep() throws Exception {
+ CompletableFuture future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+
+ // Add PositionWithDuration to the feature
+ testFeature.getOutput().add(new DeviceFeature.PositionWithDuration(new int[]{0, 25}, new int[]{0, 1000}));
+ clientFeature = new ButtplugClientDeviceFeature(mockDevice, testFeature);
+
+ Future result = clientFeature.PositionWithDuration(15, 500);
+
+ assertNotNull(result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertInstanceOf(OutputCmd.PositionWithDuration.class, captor.getValue());
+ assertEquals(15, ((OutputCmd.PositionWithDuration) captor.getValue()).getPosition());
+ assertEquals(500, ((OutputCmd.PositionWithDuration) captor.getValue()).getDuration());
+ }
+
+ @Test
+ void testPositionWithDurationFloatWithValidValue() throws Exception {
+ CompletableFuture future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+
+ // Add PositionWithDuration to the feature
+ testFeature.getOutput().add(new DeviceFeature.PositionWithDuration(new int[]{0, 25}, new int[]{0, 1000}));
+ clientFeature = new ButtplugClientDeviceFeature(mockDevice, testFeature);
+
+ Future result = clientFeature.PositionWithDurationFloat(0.8f, 500);
+
+ assertNotNull(result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertInstanceOf(OutputCmd.PositionWithDuration.class, captor.getValue());
+ assertEquals(20, ((OutputCmd.PositionWithDuration) captor.getValue()).getPosition());
+ assertEquals(500, ((OutputCmd.PositionWithDuration) captor.getValue()).getDuration());
+ }
+
+ @Test
+ void testLedWithValidStep() throws Exception {
+ CompletableFuture future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+
+ Future result = clientFeature.Led(128);
+
+ assertNotNull(result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertInstanceOf(OutputCmd.Led.class, captor.getValue());
+ assertEquals(128, ((OutputCmd.Led) captor.getValue()).getValue());
+ }
+
+ @Test
+ void testLedFloatWithValidValue() throws Exception {
+ CompletableFuture future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+
+ Future result = clientFeature.LedFloat(0.5f);
+
+ assertNotNull(result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertInstanceOf(OutputCmd.Led.class, captor.getValue());
+ assertEquals(127, ((OutputCmd.Led) captor.getValue()).getValue());
+ }
+
+ @Test
+ void testOscillateWithValidStep() throws Exception {
+ CompletableFuture future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+
+ Future result = clientFeature.Oscillate(10);
+
+ assertNotNull(result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertInstanceOf(OutputCmd.Oscillate.class, captor.getValue());
+ assertEquals(10, ((OutputCmd.Oscillate) captor.getValue()).getValue());
+ }
+
+ @Test
+ void testOscillateFloatWithValidValue() throws Exception {
+ CompletableFuture future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+
+ Future result = clientFeature.OscillateFloat(0.5f);
+
+ assertNotNull(result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertInstanceOf(OutputCmd.Oscillate.class, captor.getValue());
+ assertEquals(10, ((OutputCmd.Oscillate) captor.getValue()).getValue());
+ }
+
+ @Test
+ void testTemperatureWithValidStep() throws Exception {
+ CompletableFuture future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+
+ Future result = clientFeature.Temperature(15);
+
+ assertNotNull(result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertInstanceOf(OutputCmd.Temperature.class, captor.getValue());
+ assertEquals(15, ((OutputCmd.Temperature) captor.getValue()).getValue());
+ }
+
+ @Test
+ void testTemperatureFloatWithValidValue() throws Exception {
+ CompletableFuture future = CompletableFuture.completedFuture(mock(ButtplugMessage.class));
+ when(mockDevice.sendOutputCommand(anyInt(), any(OutputCmd.IOutputCommand.class))).thenReturn(future);
+
+ Future result = clientFeature.TemperatureFloat(0.5f);
+
+ assertNotNull(result);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(OutputCmd.IOutputCommand.class);
+ verify(mockDevice).sendOutputCommand(eq(0), captor.capture());
+ assertInstanceOf(OutputCmd.Temperature.class, captor.getValue());
+ assertEquals(15, ((OutputCmd.Temperature) captor.getValue()).getValue());
+ }
+
+ @Test
+ void testUnsupportedOutputTypeThrowsException() {
+ // Create a feature without Vibrate output
+ DeviceFeature limitedFeature = new DeviceFeature();
+ limitedFeature.setFeatureIndex(0);
+ limitedFeature.setFeatureDescription("Limited Feature");
+ limitedFeature.setOutput(new ArrayList<>());
+ limitedFeature.setInput(new ArrayList<>());
+
+ ButtplugClientDeviceFeature limited = new ButtplugClientDeviceFeature(mockDevice, limitedFeature);
+
+ assertThrows(ButtplugDeviceFeatureException.class, () -> limited.Vibrate(50));
+ }
+
+ @Test
+ void testGetDescription() {
+ assertEquals("Test Feature", clientFeature.getDescription());
+ }
+}
diff --git a/buttplug4j/src/test/java/io/github/blackspherefollower/buttplug4j/client/ButtplugClientDeviceTest.java b/buttplug4j/src/test/java/io/github/blackspherefollower/buttplug4j/client/ButtplugClientDeviceTest.java
new file mode 100644
index 0000000..46f0f52
--- /dev/null
+++ b/buttplug4j/src/test/java/io/github/blackspherefollower/buttplug4j/client/ButtplugClientDeviceTest.java
@@ -0,0 +1,309 @@
+
+package io.github.blackspherefollower.buttplug4j.client;
+
+import io.github.blackspherefollower.buttplug4j.protocol.ButtplugMessage;
+import io.github.blackspherefollower.buttplug4j.protocol.messages.Device;
+import io.github.blackspherefollower.buttplug4j.protocol.messages.DeviceFeature;
+import io.github.blackspherefollower.buttplug4j.protocol.messages.InputCommandType;
+import io.github.blackspherefollower.buttplug4j.protocol.messages.OutputCmd;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
+
+class ButtplugClientDeviceTest {
+
+ private ButtplugClient mockClient;
+ private Device testDevice;
+ private ButtplugClientDevice clientDevice;
+
+ @BeforeEach
+ void setup() {
+ mockClient = mock(ButtplugClient.class);
+
+ HashMap features = new HashMap<>();
+
+ // Feature 0: Vibrator
+ DeviceFeature vibratorFeature = new DeviceFeature();
+ vibratorFeature.setFeatureIndex(0);
+ vibratorFeature.setFeatureDescription("Vibrator");
+ ArrayList vibratorOutputs = new ArrayList<>();
+ vibratorOutputs.add(new DeviceFeature.Vibrate(new int[]{0, 100}));
+ vibratorFeature.setOutput(vibratorOutputs);
+ features.put(0, vibratorFeature);
+
+ // Feature 1: Battery sensor
+ DeviceFeature batteryFeature = new DeviceFeature();
+ batteryFeature.setFeatureIndex(1);
+ batteryFeature.setFeatureDescription("Battery");
+ ArrayList batteryInputs = new ArrayList<>();
+ ArrayList batteryCommands = new ArrayList<>();
+ batteryCommands.add(InputCommandType.READ);
+ batteryInputs.add(new DeviceFeature.Battery(batteryCommands, new int[][]{{0, 0}, {0, 100}}));
+ batteryFeature.setInput(batteryInputs);
+ features.put(1, batteryFeature);
+
+ // Create a test device with various features
+ testDevice = new Device(5, "Test Device",features, 100, "Display Name");
+
+ testDevice.setDeviceFeatures(features);
+
+ when(mockClient.getNextMsgId()).thenReturn(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+ when(mockClient.sendMessage(any(ButtplugMessage.class)))
+ .thenReturn(CompletableFuture.completedFuture(mock(ButtplugMessage.class)));
+
+ clientDevice = new ButtplugClientDevice(mockClient, testDevice);
+ }
+
+ @Test
+ void testConstructorInitializesProperties() {
+ assertEquals(5, clientDevice.getDeviceIndex());
+ assertEquals("Test Device", clientDevice.getName());
+ assertEquals("Display Name", clientDevice.getDisplayName());
+ assertEquals(Integer.valueOf(100), clientDevice.getMessageTimingGap());
+ }
+
+ @Test
+ void testConstructorWithNullDisplayName() {
+ testDevice.setDeviceDisplayName(null);
+ ButtplugClientDevice device = new ButtplugClientDevice(mockClient, testDevice);
+
+ assertEquals("Test Device", device.getDisplayName());
+ }
+
+ @Test
+ void testConstructorWithEmptyDisplayName() {
+ testDevice.setDeviceDisplayName("");
+ ButtplugClientDevice device = new ButtplugClientDevice(mockClient, testDevice);
+
+ assertEquals("Test Device", device.getDisplayName());
+ }
+
+ @Test
+ void testGetDeviceFeatures() {
+ Map features = clientDevice.getDeviceFeatures();
+
+ assertNotNull(features);
+ assertEquals(2, features.size());
+ assertTrue(features.containsKey(0));
+ assertTrue(features.containsKey(1));
+ assertEquals("Vibrator", features.get(0).getDescription());
+ assertEquals("Battery", features.get(1).getDescription());
+ }
+
+ @Test
+ void testSendStopDeviceCmd() {
+ Future result = clientDevice.sendStopDeviceCmd();
+
+ assertNotNull(result);
+ verify(mockClient).getNextMsgId();
+ verify(mockClient).sendMessage(any(ButtplugMessage.class));
+ }
+
+ @Test
+ void testSendOutputCommand() {
+ OutputCmd.Vibrate vibrateCommand = new OutputCmd.Vibrate(50);
+
+ Future result = clientDevice.sendOutputCommand(0, vibrateCommand);
+
+ assertNotNull(result);
+ verify(mockClient).getNextMsgId();
+ verify(mockClient).sendMessage(any(OutputCmd.class));
+ }
+
+ @Test
+ void testSendOutputCommandWithDifferentFeatureIndex() {
+ OutputCmd.Vibrate vibrateCommand = new OutputCmd.Vibrate(75);
+
+ Future result = clientDevice.sendOutputCommand(1, vibrateCommand);
+
+ assertNotNull(result);
+ verify(mockClient).getNextMsgId();
+ verify(mockClient).sendMessage(any(OutputCmd.class));
+ }
+
+ @Test
+ void testDeviceWithNoMessageTimingGap() {
+ testDevice.setDeviceMessageTimingGap(null);
+ ButtplugClientDevice device = new ButtplugClientDevice(mockClient, testDevice);
+
+ assertNull(device.getMessageTimingGap());
+ }
+
+ @Test
+ void testDeviceWithZeroMessageTimingGap() {
+ testDevice.setDeviceMessageTimingGap(0);
+ ButtplugClientDevice device = new ButtplugClientDevice(mockClient, testDevice);
+
+ assertEquals(Integer.valueOf(0), device.getMessageTimingGap());
+ }
+
+ @Test
+ void testDeviceWithNoFeatures() {
+ testDevice.setDeviceFeatures(new HashMap<>());
+ ButtplugClientDevice device = new ButtplugClientDevice(mockClient, testDevice);
+
+ Map features = device.getDeviceFeatures();
+ assertNotNull(features);
+ assertTrue(features.isEmpty());
+ }
+
+ @Test
+ void testDeviceWithNullFeatures() {
+ testDevice.setDeviceFeatures(null);
+ ButtplugClientDevice device = new ButtplugClientDevice(mockClient, testDevice);
+
+ Map features = device.getDeviceFeatures();
+ assertNotNull(features);
+ assertTrue(features.isEmpty());
+ }
+
+ @Test
+ void testDeviceWithMultipleOutputFeatures() {
+ HashMap multiFeatures = new HashMap<>();
+
+ // Add multiple output features
+ for (int i = 0; i < 5; i++) {
+ DeviceFeature feature = new DeviceFeature();
+ feature.setFeatureIndex(i);
+ feature.setFeatureDescription("Feature " + i);
+ ArrayList outputs = new ArrayList<>();
+ outputs.add(new DeviceFeature.Vibrate(new int[]{0, 100}));
+ feature.setOutput(outputs);
+ multiFeatures.put(i, feature);
+ }
+
+ testDevice.setDeviceFeatures(multiFeatures);
+ ButtplugClientDevice device = new ButtplugClientDevice(mockClient, testDevice);
+
+ Map features = device.getDeviceFeatures();
+ assertEquals(5, features.size());
+
+ for (int i = 0; i < 5; i++) {
+ assertTrue(features.containsKey(i));
+ assertEquals("Feature " + i, features.get(i).getDescription());
+ }
+ }
+
+ @Test
+ void testDeviceWithMixedInputOutputFeatures() {
+ HashMap mixedFeatures = new HashMap<>();
+
+ // Output feature
+ DeviceFeature outputFeature = new DeviceFeature();
+ outputFeature.setFeatureIndex(0);
+ outputFeature.setFeatureDescription("Output Feature");
+ ArrayList outputs = new ArrayList<>();
+ outputs.add(new DeviceFeature.Rotate(new int[]{0, 50}));
+ outputFeature.setOutput(outputs);
+ mixedFeatures.put(0, outputFeature);
+
+ // Input feature
+ DeviceFeature inputFeature = new DeviceFeature();
+ inputFeature.setFeatureIndex(1);
+ inputFeature.setFeatureDescription("Input Feature");
+ ArrayList inputs = new ArrayList<>();
+ ArrayList commands = new ArrayList<>();
+ commands.add(InputCommandType.READ);
+ inputs.add(new DeviceFeature.Pressure(commands, new int[][]{{0, 0}, {0, 100}}));
+ inputFeature.setInput(inputs);
+ mixedFeatures.put(1, inputFeature);
+
+ // Both input and output feature
+ DeviceFeature mixedFeature = new DeviceFeature();
+ mixedFeature.setFeatureIndex(2);
+ mixedFeature.setFeatureDescription("Mixed Feature");
+ mixedFeature.setOutput(outputs);
+ mixedFeature.setInput(inputs);
+ mixedFeatures.put(2, mixedFeature);
+
+ testDevice.setDeviceFeatures(mixedFeatures);
+ ButtplugClientDevice device = new ButtplugClientDevice(mockClient, testDevice);
+
+ Map features = device.getDeviceFeatures();
+ assertEquals(3, features.size());
+ assertEquals("Output Feature", features.get(0).getDescription());
+ assertEquals("Input Feature", features.get(1).getDescription());
+ assertEquals("Mixed Feature", features.get(2).getDescription());
+ }
+
+ @Test
+ void testGetDeviceIndex() {
+ assertEquals(5, clientDevice.getDeviceIndex());
+ }
+
+ @Test
+ void testGetName() {
+ assertEquals("Test Device", clientDevice.getName());
+ }
+
+ @Test
+ void testGetDisplayName() {
+ assertEquals("Display Name", clientDevice.getDisplayName());
+ }
+
+ @Test
+ void testGetMessageTimingGap() {
+ assertEquals(Integer.valueOf(100), clientDevice.getMessageTimingGap());
+ }
+
+ @Test
+ void testDeviceWithSpecialCharactersInName() {
+ String specialName = "Test!@#$%^&*()_+-=[]{}|;':\",./<>?";
+ testDevice.setDeviceName(specialName);
+ testDevice.setDeviceDisplayName(specialName);
+ ButtplugClientDevice device = new ButtplugClientDevice(mockClient, testDevice);
+
+ assertEquals(specialName, device.getName());
+ assertEquals(specialName, device.getDisplayName());
+ }
+
+ @Test
+ void testDeviceWithNegativeIndex() {
+ testDevice.setDeviceIndex(-1);
+ ButtplugClientDevice device = new ButtplugClientDevice(mockClient, testDevice);
+
+ assertEquals(-1, device.getDeviceIndex());
+ }
+
+ @Test
+ void testDeviceWithLargeIndex() {
+ testDevice.setDeviceIndex(Integer.MAX_VALUE);
+ ButtplugClientDevice device = new ButtplugClientDevice(mockClient, testDevice);
+
+ assertEquals(Integer.MAX_VALUE, device.getDeviceIndex());
+ }
+
+ @Test
+ void testDeviceWithLargeMessageTimingGap() {
+ testDevice.setDeviceMessageTimingGap(Integer.MAX_VALUE);
+ ButtplugClientDevice device = new ButtplugClientDevice(mockClient, testDevice);
+
+ assertEquals(Integer.valueOf(Integer.MAX_VALUE), device.getMessageTimingGap());
+ }
+
+ @Test
+ void testMultipleSendOutputCommandCalls() {
+ OutputCmd.Vibrate command1 = new OutputCmd.Vibrate(25);
+ OutputCmd.Vibrate command2 = new OutputCmd.Vibrate(50);
+ OutputCmd.Vibrate command3 = new OutputCmd.Vibrate(75);
+
+ Future result1 = clientDevice.sendOutputCommand(0, command1);
+ Future result2 = clientDevice.sendOutputCommand(0, command2);
+ Future result3 = clientDevice.sendOutputCommand(0, command3);
+
+ assertNotNull(result1);
+ assertNotNull(result2);
+ assertNotNull(result3);
+ verify(mockClient, times(3)).getNextMsgId();
+ verify(mockClient, times(3)).sendMessage(any(OutputCmd.class));
+ }
+}
diff --git a/buttplug4j/src/test/java/io/github/blackspherefollower/buttplug4j/client/ButtplugClientExceptionTest.java b/buttplug4j/src/test/java/io/github/blackspherefollower/buttplug4j/client/ButtplugClientExceptionTest.java
new file mode 100644
index 0000000..6676d42
--- /dev/null
+++ b/buttplug4j/src/test/java/io/github/blackspherefollower/buttplug4j/client/ButtplugClientExceptionTest.java
@@ -0,0 +1,22 @@
+package io.github.blackspherefollower.buttplug4j.client;
+
+import io.github.blackspherefollower.buttplug4j.ButtplugException;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class ButtplugClientExceptionTest {
+
+ @Test
+ public void testExceptionWithMessage() {
+ String errorMessage = "Client error occurred";
+ ButtplugClientException exception = new ButtplugClientException(errorMessage);
+
+ assertEquals(errorMessage, exception.getMessage());
+ }
+
+ @Test
+ public void testExceptionInheritance() {
+ ButtplugClientException exception = new ButtplugClientException("test");
+ assertInstanceOf(ButtplugException.class, exception);
+ }
+}
diff --git a/buttplug4j/src/test/java/io/github/blackspherefollower/buttplug4j/client/ButtplugClientTest.java b/buttplug4j/src/test/java/io/github/blackspherefollower/buttplug4j/client/ButtplugClientTest.java
new file mode 100644
index 0000000..b43aed4
--- /dev/null
+++ b/buttplug4j/src/test/java/io/github/blackspherefollower/buttplug4j/client/ButtplugClientTest.java
@@ -0,0 +1,487 @@
+package io.github.blackspherefollower.buttplug4j.client;
+
+import io.github.blackspherefollower.buttplug4j.protocol.ButtplugMessage;
+import io.github.blackspherefollower.buttplug4j.protocol.messages.*;
+import io.github.blackspherefollower.buttplug4j.protocol.messages.Error;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ButtplugClientTest {
+
+ private TestButtplugClient client;
+ private AtomicReference addedDevice;
+ private AtomicReference updatedDevice;
+ private AtomicReference removedDevice;
+ private AtomicBoolean scanningFinishedCalled;
+ private AtomicReference errorReceived;
+ private AtomicReference sensorReadingReceived;
+ private AtomicBoolean connectedCalled;
+
+ @BeforeEach
+ void setup() {
+ client = new TestButtplugClient("Test Client");
+ addedDevice = new AtomicReference<>();
+ updatedDevice = new AtomicReference<>();
+ removedDevice = new AtomicReference<>();
+ scanningFinishedCalled = new AtomicBoolean(false);
+ errorReceived = new AtomicReference<>();
+ sensorReadingReceived = new AtomicReference<>();
+ connectedCalled = new AtomicBoolean(false);
+ }
+
+ @Test
+ void testConstructor() {
+ assertNotNull(client);
+ assertEquals(ButtplugClient.ConnectionState.DISCONNECTED, client.getConnectionState());
+ assertFalse(client.isConnected());
+ }
+
+ @Test
+ void testGetNextMsgId() {
+ assertEquals(1, client.getNextMsgId());
+ assertEquals(2, client.getNextMsgId());
+ assertEquals(3, client.getNextMsgId());
+ }
+
+ @Test
+ void testConnectionStates() {
+ assertEquals(ButtplugClient.ConnectionState.DISCONNECTED, client.getConnectionState());
+ assertFalse(client.isConnected());
+
+ client.setConnectionState(ButtplugClient.ConnectionState.CONNECTING);
+ assertEquals(ButtplugClient.ConnectionState.CONNECTING, client.getConnectionState());
+ assertFalse(client.isConnected());
+
+ client.setConnectionState(ButtplugClient.ConnectionState.CONNECTED);
+ assertEquals(ButtplugClient.ConnectionState.CONNECTED, client.getConnectionState());
+ assertTrue(client.isConnected());
+ }
+
+ @Test
+ void testSetAndGetDeviceAddedHandler() {
+ IDeviceAddedEvent handler = device -> addedDevice.set(device);
+ client.setDeviceAdded(handler);
+ assertEquals(handler, client.getDeviceAdded());
+ }
+
+ @Test
+ void testSetAndGetDeviceRemovedHandler() {
+ IDeviceRemovedEvent handler = device -> removedDevice.set(device);
+ client.setDeviceRemoved(handler);
+ assertEquals(handler, client.getDeviceRemoved());
+ }
+
+ @Test
+ void testSetAndGetScanningFinishedHandler() {
+ IScanningEvent handler = () -> scanningFinishedCalled.set(true);
+ client.setScanningFinished(handler);
+ assertEquals(handler, client.getScanningFinished());
+ }
+
+ @Test
+ void testSetAndGetErrorReceivedHandler() {
+ IErrorEvent handler = error -> errorReceived.set(error);
+ client.setErrorReceived(handler);
+ assertEquals(handler, client.getErrorReceived());
+ }
+
+ @Test
+ void testSetAndGetSensorReadingReceivedHandler() {
+ ISensorReadingEvent handler = reading -> sensorReadingReceived.set(reading);
+ client.setSensorReadingReceived(handler);
+ assertEquals(handler, client.getSensorReadingReceived());
+ }
+
+ @Test
+ void testSetAndGetOnConnectedHandler() {
+ IConnectedEvent handler = c -> connectedCalled.set(true);
+ client.setOnConnected(handler);
+ assertEquals(handler, client.getOnConnected());
+ }
+
+ @Test
+ void testOnMessageWithOk() {
+ AtomicInteger completedId = new AtomicInteger(-1);
+ CompletableFuture future = new CompletableFuture<>();
+ future.thenAccept(msg -> {
+ if (msg instanceof Ok) {
+ completedId.set(msg.getId());
+ }
+ });
+
+ client.scheduleWait(5, future);
+
+ List messages = new ArrayList<>();
+ messages.add(new Ok(5));
+ client.onMessage(messages);
+
+ assertTrue(future.isDone());
+ assertEquals(5, completedId.get());
+ }
+
+ @Test
+ void testOnMessageWithError() {
+ client.setErrorReceived(error -> errorReceived.set(error));
+
+ List messages = new ArrayList<>();
+ Error error = new Error("Test error", Error.ErrorClass.ERROR_DEVICE, 0);
+ messages.add(error);
+ client.onMessage(messages);
+
+ assertNotNull(errorReceived.get());
+ assertEquals("Test error", errorReceived.get().getErrorMessage());
+ }
+
+ @Test
+ void testOnMessageWithScanningFinished() {
+ client.setScanningFinished(() -> scanningFinishedCalled.set(true));
+
+ List