Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add battery reading wrapper #46

Merged
merged 3 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package io.github.blackspherefollower.buttplug4j.connectors.jetty.websocket.client;

import io.github.blackspherefollower.buttplug4j.client.ButtplugClientDevice;
import io.github.blackspherefollower.buttplug4j.client.ButtplugDeviceException;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import java.net.URI;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class ButtplugClientWSJettyClientTest {
Expand Down Expand Up @@ -50,4 +53,29 @@ public void TestConnect() throws Exception {

client.disconnect();
}

@Disabled
@Test
public void TestBattery() throws Exception {
ButtplugClientWSClient client = new ButtplugClientWSClient("Java Test");
client.connect(new URI("ws://localhost:12345/buttplug"));
client.startScanning();

Thread.sleep(2000);
client.requestDeviceList();
for (ButtplugClientDevice dev : client.getDevices()) {
if (dev.hasBatterySensor()) {
long battery = dev.readBatteryLevel();
System.out.println("Battery is " + battery);
assertTrue(battery >= 0);
assertTrue(battery <= 100);
} else {
assertThrows(ButtplugDeviceException.class, () -> {
long battery = dev.readBatteryLevel();
});
}
}

client.disconnect();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;


public class ButtplugClientDevice {
Expand Down Expand Up @@ -382,6 +385,117 @@ public final Future<ButtplugMessage> sendLinearCmd(final Pair<Double, Long>[] ve
values.toArray(new LinearCmd.LinearSubCmd[]{}), client.getNextMsgId()));
}

/**
* Sends a command to read a sensor value from the device.
* <p>
* 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.
* </p>
*
* @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<ButtplugMessage> 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!");
}

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.
* <p>
* 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".
* </p>
*
* @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)) {
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.
* <p>
* 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.
* </p>
*
* @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;
}
}

if (!found) {
throw new ButtplugDeviceException("Device doesn't have Battery feature!");
}

Future<ButtplugMessage> 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");
}
Expand Down
Loading