buttplug 3.2.2 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +25 -25
- package/.jscsrc +2 -2
- package/.jshintrc +5 -5
- package/.prettierrc.json +3 -3
- package/.yarnrc.yml +5 -5
- package/CHANGELOG.md +577 -577
- package/LICENSE +27 -27
- package/README.md +97 -97
- package/dist/main/src/client/ButtplugBrowserWebsocketClientConnector.js +6 -9
- package/dist/main/src/client/ButtplugBrowserWebsocketClientConnector.js.map +1 -1
- package/dist/main/src/client/{Client.d.ts → ButtplugClient.d.ts} +3 -4
- package/dist/main/src/client/ButtplugClient.js +227 -0
- package/dist/main/src/client/ButtplugClient.js.map +1 -0
- package/dist/main/src/client/ButtplugClientConnectorException.js +17 -7
- package/dist/main/src/client/ButtplugClientConnectorException.js.map +1 -1
- package/dist/main/src/client/ButtplugClientDevice.d.ts +13 -28
- package/dist/main/src/client/ButtplugClientDevice.js +105 -247
- package/dist/main/src/client/ButtplugClientDevice.js.map +1 -1
- package/dist/main/src/client/ButtplugClientDeviceCommand.d.ts +42 -0
- package/dist/main/src/client/ButtplugClientDeviceCommand.js +105 -0
- package/dist/main/src/client/ButtplugClientDeviceCommand.js.map +1 -0
- package/dist/main/src/client/ButtplugClientDeviceFeature.d.ts +18 -0
- package/dist/main/src/client/ButtplugClientDeviceFeature.js +166 -0
- package/dist/main/src/client/ButtplugClientDeviceFeature.js.map +1 -0
- package/dist/main/src/client/ButtplugNodeWebsocketClientConnector.d.ts +1 -8
- package/dist/main/src/client/ButtplugNodeWebsocketClientConnector.js +1 -4
- package/dist/main/src/client/ButtplugNodeWebsocketClientConnector.js.map +1 -1
- package/dist/main/src/core/Exceptions.js +27 -9
- package/dist/main/src/core/Exceptions.js.map +1 -1
- package/dist/main/src/core/Logging.js +12 -6
- package/dist/main/src/core/Logging.js.map +1 -1
- package/dist/main/src/core/Messages.d.ts +118 -228
- package/dist/main/src/core/Messages.js +51 -404
- package/dist/main/src/core/Messages.js.map +1 -1
- package/dist/main/src/index.d.ts +2 -2
- package/dist/main/src/index.js +4 -2
- package/dist/main/src/index.js.map +1 -1
- package/dist/main/src/utils/ButtplugBrowserWebsocketConnector.js +40 -52
- package/dist/main/src/utils/ButtplugBrowserWebsocketConnector.js.map +1 -1
- package/dist/main/src/utils/ButtplugMessageSorter.js +27 -15
- package/dist/main/src/utils/ButtplugMessageSorter.js.map +1 -1
- package/dist/main/src/utils/Utils.js +1 -2
- package/dist/main/src/utils/Utils.js.map +1 -1
- package/dist/web/buttplug.js +1 -38
- package/dist/web/buttplug.mjs +595 -1984
- package/dist/web/client/ButtplugBrowserWebsocketClientConnector.d.ts +0 -7
- package/dist/web/client/{Client.d.ts → ButtplugClient.d.ts} +3 -11
- package/dist/web/client/ButtplugClientConnectorException.d.ts +0 -7
- package/dist/web/client/ButtplugClientDevice.d.ts +14 -29
- package/dist/web/client/ButtplugClientDeviceCommand.d.ts +42 -0
- package/dist/web/client/ButtplugClientDeviceFeature.d.ts +18 -0
- package/dist/web/client/ButtplugNodeWebsocketClientConnector.d.ts +1 -15
- package/dist/web/client/IButtplugClientConnector.d.ts +0 -7
- package/dist/web/core/Exceptions.d.ts +1 -1
- package/dist/web/core/Logging.d.ts +0 -7
- package/dist/web/core/Messages.d.ts +118 -227
- package/dist/web/index.d.ts +2 -2
- package/dist/web/utils/ButtplugBrowserWebsocketConnector.d.ts +0 -7
- package/examples/node/SYNC_MANIFEST.md +105 -0
- package/examples/node/application-example.ts +213 -0
- package/examples/node/async-example.ts +124 -0
- package/examples/node/connection-example.ts +76 -0
- package/examples/node/device-control-example.ts +131 -0
- package/examples/node/device-enumeration-example.ts +86 -0
- package/examples/node/device-info-example.ts +131 -0
- package/examples/node/errors-example.ts +166 -0
- package/examples/node/package-lock.json +281 -0
- package/examples/node/package.json +25 -0
- package/examples/node/remote-connector-example.ts +84 -0
- package/examples/node/tsconfig.json +14 -0
- package/examples/web/application-example.js +197 -0
- package/examples/web/async-example.js +90 -0
- package/examples/web/device-control-example.js +87 -0
- package/examples/web/device-enumeration-example.js +49 -0
- package/examples/web/device-info-example.js +100 -0
- package/examples/web/errors-example.js +110 -0
- package/examples/web/index.html +55 -0
- package/examples/web/logging.js +42 -0
- package/examples/web/ping-timeout-example.js +59 -0
- package/examples/web/remote-connector-example.js +68 -0
- package/node-test.js +24 -0
- package/node-test.ts +23 -5
- package/package.json +85 -87
- package/src/client/ButtplugBrowserWebsocketClientConnector.ts +25 -25
- package/src/client/ButtplugClient.ts +242 -0
- package/src/client/ButtplugClientConnectorException.ts +16 -16
- package/src/client/ButtplugClientDevice.ts +178 -401
- package/src/client/ButtplugClientDeviceCommand.ts +112 -0
- package/src/client/ButtplugClientDeviceFeature.ts +138 -0
- package/src/client/ButtplugNodeWebsocketClientConnector.ts +17 -17
- package/src/client/IButtplugClientConnector.ts +18 -18
- package/src/core/Exceptions.ts +107 -101
- package/src/core/Logging.ts +197 -197
- package/src/core/Messages.ts +209 -480
- package/src/core/index.d.ts +4 -4
- package/src/index.ts +21 -19
- package/src/utils/ButtplugBrowserWebsocketConnector.ts +89 -89
- package/src/utils/ButtplugMessageSorter.ts +66 -65
- package/src/utils/Utils.ts +3 -3
- package/tsconfig.json +22 -22
- package/tsfmt.json +14 -14
- package/tslint.json +27 -27
- package/typedocconfig.js +6 -6
- package/vite.config.ts +26 -26
- package/dist/main/src/client/Client.js +0 -242
- package/dist/main/src/client/Client.js.map +0 -1
- package/dist/main/src/core/MessageUtils.d.ts +0 -10
- package/dist/main/src/core/MessageUtils.js +0 -65
- package/dist/main/src/core/MessageUtils.js.map +0 -1
- package/dist/web/core/MessageUtils.d.ts +0 -10
- package/doc/.nojekyll +0 -1
- package/doc/assets/highlight.css +0 -22
- package/doc/assets/main.js +0 -58
- package/doc/assets/search.js +0 -1
- package/doc/assets/style.css +0 -1280
- package/doc/classes/ButtplugBrowserWebsocketClientConnector.html +0 -234
- package/doc/classes/ButtplugClient.html +0 -331
- package/doc/classes/ButtplugClientConnectorException.html +0 -216
- package/doc/classes/ButtplugClientDevice.html +0 -489
- package/doc/classes/ButtplugDeviceError.html +0 -218
- package/doc/classes/ButtplugDeviceMessage.html +0 -165
- package/doc/classes/ButtplugError.html +0 -220
- package/doc/classes/ButtplugInitError.html +0 -218
- package/doc/classes/ButtplugLogger.html +0 -288
- package/doc/classes/ButtplugMessage.html +0 -147
- package/doc/classes/ButtplugMessageError.html +0 -218
- package/doc/classes/ButtplugMessageSorter.html +0 -128
- package/doc/classes/ButtplugNodeWebsocketClientConnector.html +0 -239
- package/doc/classes/ButtplugPingError.html +0 -218
- package/doc/classes/ButtplugSystemMessage.html +0 -150
- package/doc/classes/ButtplugUnknownError.html +0 -218
- package/doc/classes/DeviceAdded.html +0 -186
- package/doc/classes/DeviceInfo.html +0 -114
- package/doc/classes/DeviceList.html +0 -160
- package/doc/classes/DeviceRemoved.html +0 -158
- package/doc/classes/Error.html +0 -179
- package/doc/classes/GenericDeviceMessageAttributes.html +0 -107
- package/doc/classes/GenericMessageSubcommand.html +0 -90
- package/doc/classes/LinearCmd.html +0 -187
- package/doc/classes/LogMessage.html +0 -134
- package/doc/classes/MessageAttributes.html +0 -160
- package/doc/classes/Ok.html +0 -151
- package/doc/classes/Ping.html +0 -151
- package/doc/classes/RawDeviceMessageAttributes.html +0 -86
- package/doc/classes/RawReadCmd.html +0 -188
- package/doc/classes/RawReading.html +0 -179
- package/doc/classes/RawSubscribeCmd.html +0 -170
- package/doc/classes/RawUnsubscribeCmd.html +0 -170
- package/doc/classes/RawWriteCmd.html +0 -188
- package/doc/classes/RequestDeviceList.html +0 -151
- package/doc/classes/RequestServerInfo.html +0 -169
- package/doc/classes/RotateCmd.html +0 -187
- package/doc/classes/RotateSubcommand.html +0 -108
- package/doc/classes/ScalarCmd.html +0 -170
- package/doc/classes/ScalarSubcommand.html +0 -108
- package/doc/classes/ScanningFinished.html +0 -146
- package/doc/classes/SensorDeviceMessageAttributes.html +0 -107
- package/doc/classes/SensorReadCmd.html +0 -179
- package/doc/classes/SensorReading.html +0 -188
- package/doc/classes/ServerInfo.html +0 -178
- package/doc/classes/StartScanning.html +0 -151
- package/doc/classes/StopAllDevices.html +0 -151
- package/doc/classes/StopDeviceCmd.html +0 -161
- package/doc/classes/StopScanning.html +0 -151
- package/doc/classes/VectorSubcommand.html +0 -108
- package/doc/enums/ActuatorType.html +0 -104
- package/doc/enums/ButtplugLogLevel.html +0 -97
- package/doc/enums/ErrorClass.html +0 -90
- package/doc/enums/SensorType.html +0 -90
- package/doc/functions/FromJSON.html +0 -113
- package/doc/index.html +0 -184
- package/doc/interfaces/IButtplugClientConnector.html +0 -137
- package/doc/modules.html +0 -176
- package/doc/variables/DEFAULT_MESSAGE_ID.html +0 -104
- package/doc/variables/MAX_ID.html +0 -104
- package/doc/variables/MESSAGE_SPEC_VERSION.html +0 -104
- package/doc/variables/SYSTEM_MESSAGE_ID.html +0 -104
- package/src/client/Client.ts +0 -276
- package/src/core/MessageUtils.ts +0 -48
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Buttplug JS Source Code File - Visit https://buttplug.io for more info about
|
|
3
|
-
* the project. Licensed under the BSD 3-Clause license. See LICENSE file in the
|
|
4
|
-
* project root for full license information.
|
|
5
|
-
*
|
|
6
|
-
* @copyright Copyright (c) Nonpolynomial Labs LLC. All rights reserved.
|
|
7
|
-
*/
|
|
8
1
|
import { EventEmitter } from 'eventemitter3';
|
|
9
2
|
import { ButtplugMessage } from '../core/Messages';
|
|
10
3
|
export declare class ButtplugBrowserWebsocketConnector extends EventEmitter {
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Buttplug TypeScript/JavaScript Examples
|
|
2
|
+
|
|
3
|
+
These examples demonstrate how to use the Buttplug TypeScript/JavaScript library for Node.js CLI applications.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
1. Install [Intiface Central](https://intiface.com/central)
|
|
8
|
+
2. Start the server in Intiface Central
|
|
9
|
+
3. Install dependencies: `npm install`
|
|
10
|
+
|
|
11
|
+
## Running Examples
|
|
12
|
+
|
|
13
|
+
Each example can be run with ts-node:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx ts-node --esm <example-name>.ts
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or use the npm scripts:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm run connection # Basic connection example
|
|
23
|
+
npm run device-enumeration # Device scanning and events
|
|
24
|
+
npm run device-control # Sending commands to devices
|
|
25
|
+
npm run device-info # Device feature introspection
|
|
26
|
+
npm run async # Async/await patterns
|
|
27
|
+
npm run errors # Error handling patterns
|
|
28
|
+
npm run remote-connector # Explicit connector setup
|
|
29
|
+
npm run application # Complete interactive demo
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Examples
|
|
33
|
+
|
|
34
|
+
| File | Description |
|
|
35
|
+
|------|-------------|
|
|
36
|
+
| `connection-example.ts` | Basic WebSocket connection and error handling |
|
|
37
|
+
| `device-enumeration-example.ts` | Device scanning and event listening |
|
|
38
|
+
| `device-control-example.ts` | Sending commands and capability queries |
|
|
39
|
+
| `device-info-example.ts` | Device feature introspection |
|
|
40
|
+
| `async-example.ts` | Async/await patterns and event handling |
|
|
41
|
+
| `errors-example.ts` | Error type reference and handling patterns |
|
|
42
|
+
| `remote-connector-example.ts` | Explicit connector setup |
|
|
43
|
+
| `application-example.ts` | Complete interactive CLI demo |
|
|
44
|
+
|
|
45
|
+
## Key API Patterns
|
|
46
|
+
|
|
47
|
+
### Connection
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import {
|
|
51
|
+
ButtplugClient,
|
|
52
|
+
ButtplugNodeWebsocketClientConnector,
|
|
53
|
+
} from 'buttplug';
|
|
54
|
+
|
|
55
|
+
const client = new ButtplugClient('App Name');
|
|
56
|
+
const connector = new ButtplugNodeWebsocketClientConnector('ws://127.0.0.1:12345');
|
|
57
|
+
await client.connect(connector);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Events
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
client.addListener('deviceadded', (device) => {
|
|
64
|
+
console.log(`Device connected: ${device.name}`);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
client.addListener('deviceremoved', (device) => {
|
|
68
|
+
console.log(`Device disconnected: ${device.name}`);
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Device Control
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { DeviceOutput, OutputType, InputType } from 'buttplug';
|
|
76
|
+
|
|
77
|
+
// Check capabilities
|
|
78
|
+
if (device.hasOutput(OutputType.Vibrate)) {
|
|
79
|
+
// Send command using the fluent builder API
|
|
80
|
+
await device.runOutput(DeviceOutput.Vibrate.percent(0.5));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Stop the device
|
|
84
|
+
await device.stop();
|
|
85
|
+
|
|
86
|
+
// Read battery level
|
|
87
|
+
if (device.hasInput(InputType.Battery)) {
|
|
88
|
+
const battery = await device.battery();
|
|
89
|
+
console.log(`Battery: ${battery * 100}%`);
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Node.js vs Browser
|
|
94
|
+
|
|
95
|
+
- **Node.js**: Use `ButtplugNodeWebsocketClientConnector` (requires `ws` package)
|
|
96
|
+
- **Browser**: Use `ButtplugBrowserWebsocketClientConnector` (uses native WebSocket)
|
|
97
|
+
|
|
98
|
+
## Syncing to Documentation
|
|
99
|
+
|
|
100
|
+
These examples are synced to the Buttplug documentation site using the sync script in docs.buttplug.io:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
cd ../docs.buttplug.io
|
|
104
|
+
./scripts/sync-examples.sh
|
|
105
|
+
```
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// Buttplug TypeScript - Complete Application Example
|
|
2
|
+
//
|
|
3
|
+
// This is a complete, working example that demonstrates the full workflow
|
|
4
|
+
// of a Buttplug application. If you're new to Buttplug, start here!
|
|
5
|
+
//
|
|
6
|
+
// Prerequisites:
|
|
7
|
+
// 1. Install Intiface Central: https://intiface.com/central
|
|
8
|
+
// 2. Start the server in Intiface Central (click "Start Server")
|
|
9
|
+
// 3. Run: npx ts-node --esm application-example.ts
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
ButtplugClient,
|
|
13
|
+
ButtplugNodeWebsocketClientConnector,
|
|
14
|
+
ButtplugClientDevice,
|
|
15
|
+
ButtplugClientConnectorException,
|
|
16
|
+
ButtplugDeviceError,
|
|
17
|
+
ButtplugError,
|
|
18
|
+
DeviceOutput,
|
|
19
|
+
OutputType,
|
|
20
|
+
InputType,
|
|
21
|
+
} from 'buttplug';
|
|
22
|
+
import * as readline from 'readline';
|
|
23
|
+
|
|
24
|
+
function createReadlineInterface(): readline.Interface {
|
|
25
|
+
return readline.createInterface({
|
|
26
|
+
input: process.stdin,
|
|
27
|
+
output: process.stdout,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function waitForEnter(prompt: string): Promise<void> {
|
|
32
|
+
const rl = createReadlineInterface();
|
|
33
|
+
return new Promise((resolve) => {
|
|
34
|
+
rl.question(prompt, () => {
|
|
35
|
+
rl.close();
|
|
36
|
+
resolve();
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function prompt(question: string): Promise<string> {
|
|
42
|
+
const rl = createReadlineInterface();
|
|
43
|
+
return new Promise((resolve) => {
|
|
44
|
+
rl.question(question, (answer) => {
|
|
45
|
+
rl.close();
|
|
46
|
+
resolve(answer);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function main(): Promise<void> {
|
|
52
|
+
console.log('===========================================');
|
|
53
|
+
console.log(' Buttplug TypeScript Application Example');
|
|
54
|
+
console.log('===========================================\n');
|
|
55
|
+
|
|
56
|
+
// Step 1: Create a client
|
|
57
|
+
// The client name identifies your application to the server.
|
|
58
|
+
const client = new ButtplugClient('My Buttplug Application');
|
|
59
|
+
|
|
60
|
+
// Step 2: Set up event handlers
|
|
61
|
+
// Always do this BEFORE connecting to avoid missing events.
|
|
62
|
+
client.addListener('deviceadded', (device: ButtplugClientDevice) => {
|
|
63
|
+
console.log(`[+] Device connected: ${device.name}`);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
client.addListener('deviceremoved', (device: ButtplugClientDevice) => {
|
|
67
|
+
console.log(`[-] Device disconnected: ${device.name}`);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
client.addListener('disconnect', () => {
|
|
71
|
+
console.log('[!] Server connection lost!');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Step 3: Connect to the server
|
|
75
|
+
console.log('Connecting to Intiface Central...');
|
|
76
|
+
try {
|
|
77
|
+
const connector = new ButtplugNodeWebsocketClientConnector(
|
|
78
|
+
'ws://127.0.0.1:12345'
|
|
79
|
+
);
|
|
80
|
+
await client.connect(connector);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
if (e instanceof ButtplugClientConnectorException) {
|
|
83
|
+
console.log('ERROR: Could not connect to Intiface Central!');
|
|
84
|
+
console.log(
|
|
85
|
+
'Make sure Intiface Central is running and the server is started.'
|
|
86
|
+
);
|
|
87
|
+
console.log('Default address: ws://127.0.0.1:12345');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
throw e;
|
|
91
|
+
}
|
|
92
|
+
console.log('Connected!\n');
|
|
93
|
+
|
|
94
|
+
// Step 4: Scan for devices
|
|
95
|
+
console.log('Scanning for devices...');
|
|
96
|
+
console.log('Turn on your Bluetooth/USB devices now.\n');
|
|
97
|
+
await client.startScanning();
|
|
98
|
+
|
|
99
|
+
// Wait for devices
|
|
100
|
+
await waitForEnter('Press Enter when your devices are connected...');
|
|
101
|
+
await client.stopScanning();
|
|
102
|
+
|
|
103
|
+
// Step 5: Check what devices we found
|
|
104
|
+
const devices = Array.from(client.devices.values());
|
|
105
|
+
if (devices.length === 0) {
|
|
106
|
+
console.log('No devices found. Make sure your device is:');
|
|
107
|
+
console.log(' - Turned on');
|
|
108
|
+
console.log(' - In pairing/discoverable mode');
|
|
109
|
+
console.log(' - Supported by Buttplug (check https://iostindex.com)');
|
|
110
|
+
await client.disconnect();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
console.log(`\nFound ${devices.length} device(s):\n`);
|
|
115
|
+
|
|
116
|
+
// Step 6: Display device capabilities
|
|
117
|
+
for (const device of devices) {
|
|
118
|
+
console.log(` ${device.name}`);
|
|
119
|
+
|
|
120
|
+
// Check output capabilities (things we can make the device do)
|
|
121
|
+
const outputs: string[] = [];
|
|
122
|
+
if (device.hasOutput(OutputType.Vibrate)) outputs.push('Vibrate');
|
|
123
|
+
if (device.hasOutput(OutputType.Rotate)) outputs.push('Rotate');
|
|
124
|
+
if (device.hasOutput(OutputType.Oscillate)) outputs.push('Oscillate');
|
|
125
|
+
if (device.hasOutput(OutputType.Position)) outputs.push('Position');
|
|
126
|
+
if (device.hasOutput(OutputType.Constrict)) outputs.push('Constrict');
|
|
127
|
+
|
|
128
|
+
if (outputs.length > 0) {
|
|
129
|
+
console.log(` Outputs: ${outputs.join(', ')}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Check input capabilities (sensors we can read)
|
|
133
|
+
const inputs: string[] = [];
|
|
134
|
+
if (device.hasInput(InputType.Battery)) inputs.push('Battery');
|
|
135
|
+
if (device.hasInput(InputType.RSSI)) inputs.push('RSSI');
|
|
136
|
+
if (device.hasInput(InputType.Button)) inputs.push('Button');
|
|
137
|
+
if (device.hasInput(InputType.Pressure)) inputs.push('Pressure');
|
|
138
|
+
|
|
139
|
+
if (inputs.length > 0) {
|
|
140
|
+
console.log(` Inputs: ${inputs.join(', ')}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
console.log();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Step 7: Interactive device control
|
|
147
|
+
console.log('=== Interactive Control ===');
|
|
148
|
+
console.log('Commands:');
|
|
149
|
+
console.log(' v <0-100> - Vibrate all devices at percentage');
|
|
150
|
+
console.log(' s - Stop all devices');
|
|
151
|
+
console.log(' b - Read battery levels');
|
|
152
|
+
console.log(' q - Quit\n');
|
|
153
|
+
|
|
154
|
+
let running = true;
|
|
155
|
+
while (running) {
|
|
156
|
+
const input = (await prompt('> ')).trim().toLowerCase();
|
|
157
|
+
|
|
158
|
+
if (!input) continue;
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
if (input.startsWith('v ')) {
|
|
162
|
+
// Vibrate command
|
|
163
|
+
const percentStr = input.slice(2);
|
|
164
|
+
const percent = parseInt(percentStr, 10);
|
|
165
|
+
if (!isNaN(percent) && percent >= 0 && percent <= 100) {
|
|
166
|
+
const intensity = percent / 100.0;
|
|
167
|
+
for (const device of devices) {
|
|
168
|
+
if (device.hasOutput(OutputType.Vibrate)) {
|
|
169
|
+
await device.runOutput(DeviceOutput.Vibrate.percent(intensity));
|
|
170
|
+
console.log(` ${device.name}: vibrating at ${percent}%`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
console.log(' Usage: v <0-100>');
|
|
175
|
+
}
|
|
176
|
+
} else if (input === 's') {
|
|
177
|
+
// Stop all devices
|
|
178
|
+
await client.stopAllDevices();
|
|
179
|
+
console.log(' All devices stopped.');
|
|
180
|
+
} else if (input === 'b') {
|
|
181
|
+
// Read battery levels
|
|
182
|
+
for (const device of devices) {
|
|
183
|
+
if (device.hasInput(InputType.Battery)) {
|
|
184
|
+
const battery = await device.battery();
|
|
185
|
+
console.log(` ${device.name}: ${(battery * 100).toFixed(0)}% battery`);
|
|
186
|
+
} else {
|
|
187
|
+
console.log(` ${device.name}: no battery sensor`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
} else if (input === 'q') {
|
|
191
|
+
running = false;
|
|
192
|
+
} else {
|
|
193
|
+
console.log(' Unknown command. Use v, s, b, or q.');
|
|
194
|
+
}
|
|
195
|
+
} catch (e) {
|
|
196
|
+
if (e instanceof ButtplugDeviceError) {
|
|
197
|
+
console.log(` Device error: ${e.message}`);
|
|
198
|
+
} else if (e instanceof ButtplugError) {
|
|
199
|
+
console.log(` Error: ${e.message}`);
|
|
200
|
+
} else {
|
|
201
|
+
throw e;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Step 8: Clean up
|
|
207
|
+
console.log('\nStopping devices and disconnecting...');
|
|
208
|
+
await client.stopAllDevices();
|
|
209
|
+
await client.disconnect();
|
|
210
|
+
console.log('Goodbye!');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// Buttplug TypeScript - Async Patterns Example
|
|
2
|
+
//
|
|
3
|
+
// This example demonstrates async/await patterns and event handling
|
|
4
|
+
// in the Buttplug library. The library is fully async - all operations
|
|
5
|
+
// that might block (network, device communication) use async/await.
|
|
6
|
+
//
|
|
7
|
+
// Prerequisites:
|
|
8
|
+
// 1. Install Intiface Central: https://intiface.com/central
|
|
9
|
+
// 2. Start the server in Intiface Central
|
|
10
|
+
// 3. Run: npx ts-node --esm async-example.ts
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
ButtplugClient,
|
|
14
|
+
ButtplugNodeWebsocketClientConnector,
|
|
15
|
+
ButtplugClientDevice,
|
|
16
|
+
DeviceOutput,
|
|
17
|
+
OutputType,
|
|
18
|
+
} from 'buttplug';
|
|
19
|
+
import * as readline from 'readline';
|
|
20
|
+
|
|
21
|
+
async function waitForEnter(prompt: string): Promise<void> {
|
|
22
|
+
const rl = readline.createInterface({
|
|
23
|
+
input: process.stdin,
|
|
24
|
+
output: process.stdout,
|
|
25
|
+
});
|
|
26
|
+
return new Promise((resolve) => {
|
|
27
|
+
rl.question(prompt, () => {
|
|
28
|
+
rl.close();
|
|
29
|
+
resolve();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function delay(ms: number): Promise<void> {
|
|
35
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function main(): Promise<void> {
|
|
39
|
+
const client = new ButtplugClient('Async Example');
|
|
40
|
+
|
|
41
|
+
// Events in buttplug-js use EventEmitter3.
|
|
42
|
+
// You can use addListener or on to subscribe to events.
|
|
43
|
+
|
|
44
|
+
// 'deviceadded' is fired when a new device connects
|
|
45
|
+
client.addListener('deviceadded', async (device: ButtplugClientDevice) => {
|
|
46
|
+
// Note: Event handlers can be async!
|
|
47
|
+
console.log(`[Event] Device added: ${device.name}`);
|
|
48
|
+
|
|
49
|
+
// You can interact with the device in the event handler
|
|
50
|
+
if (device.hasOutput(OutputType.Vibrate)) {
|
|
51
|
+
console.log(' Sending welcome vibration...');
|
|
52
|
+
await device.runOutput(DeviceOutput.Vibrate.percent(0.25));
|
|
53
|
+
await delay(200);
|
|
54
|
+
await device.stop();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// 'deviceremoved' is fired when a device disconnects
|
|
59
|
+
client.addListener('deviceremoved', (device: ButtplugClientDevice) => {
|
|
60
|
+
console.log(`[Event] Device removed: ${device.name}`);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// 'scanningfinished' is fired when scanning completes
|
|
64
|
+
// (some protocols scan continuously until stopped)
|
|
65
|
+
client.addListener('scanningfinished', () => {
|
|
66
|
+
console.log('[Event] Scanning finished');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// 'disconnect' is fired when the server connection drops
|
|
70
|
+
client.addListener('disconnect', () => {
|
|
71
|
+
console.log('[Event] Server disconnected!');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// 'inputreading' is fired when subscribed sensor data arrives
|
|
75
|
+
client.addListener('inputreading', (reading: unknown) => {
|
|
76
|
+
console.log(`[Event] Input reading: ${JSON.stringify(reading)}`);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Connect asynchronously - this may take time due to network
|
|
80
|
+
console.log('Connecting to server...');
|
|
81
|
+
const connector = new ButtplugNodeWebsocketClientConnector(
|
|
82
|
+
'ws://127.0.0.1:12345'
|
|
83
|
+
);
|
|
84
|
+
await client.connect(connector);
|
|
85
|
+
console.log('Connected!\n');
|
|
86
|
+
|
|
87
|
+
// Scanning is also async - we start it and wait for events
|
|
88
|
+
console.log('Starting scan. Turn on devices now...');
|
|
89
|
+
console.log('(Events will be printed as devices connect)\n');
|
|
90
|
+
await client.startScanning();
|
|
91
|
+
|
|
92
|
+
// Wait for user input
|
|
93
|
+
await waitForEnter('Press Enter to stop scanning...');
|
|
94
|
+
await client.stopScanning();
|
|
95
|
+
|
|
96
|
+
// Demonstrate concurrent operations
|
|
97
|
+
console.log('\nDemonstrating concurrent device control...');
|
|
98
|
+
const devices = Array.from(client.devices.values());
|
|
99
|
+
|
|
100
|
+
if (devices.length > 0) {
|
|
101
|
+
// Send commands to all devices concurrently
|
|
102
|
+
const tasks = devices
|
|
103
|
+
.filter((d) => d.hasOutput(OutputType.Vibrate))
|
|
104
|
+
.map(async (device) => {
|
|
105
|
+
console.log(` Vibrating ${device.name}...`);
|
|
106
|
+
await device.runOutput(DeviceOutput.Vibrate.percent(0.5));
|
|
107
|
+
await delay(500);
|
|
108
|
+
await device.stop();
|
|
109
|
+
console.log(` ${device.name} stopped.`);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Wait for all commands to complete
|
|
113
|
+
await Promise.all(tasks);
|
|
114
|
+
console.log('All devices stopped.');
|
|
115
|
+
} else {
|
|
116
|
+
console.log('No devices connected.');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
await waitForEnter('\nPress Enter to disconnect...');
|
|
120
|
+
await client.disconnect();
|
|
121
|
+
console.log('Disconnected.');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Buttplug TypeScript - Connection Example
|
|
2
|
+
//
|
|
3
|
+
// This example demonstrates how to connect to a Buttplug server
|
|
4
|
+
// (like Intiface Central) and handle connection errors.
|
|
5
|
+
//
|
|
6
|
+
// Prerequisites:
|
|
7
|
+
// 1. Install Intiface Central: https://intiface.com/central
|
|
8
|
+
// 2. Start the server in Intiface Central
|
|
9
|
+
// 3. Run: npx ts-node --esm connection-example.ts
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
ButtplugClient,
|
|
13
|
+
ButtplugNodeWebsocketClientConnector,
|
|
14
|
+
ButtplugClientConnectorException,
|
|
15
|
+
ButtplugError,
|
|
16
|
+
ButtplugInitError,
|
|
17
|
+
} from 'buttplug';
|
|
18
|
+
import * as readline from 'readline';
|
|
19
|
+
|
|
20
|
+
async function waitForEnter(prompt: string): Promise<void> {
|
|
21
|
+
const rl = readline.createInterface({
|
|
22
|
+
input: process.stdin,
|
|
23
|
+
output: process.stdout,
|
|
24
|
+
});
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
rl.question(prompt, () => {
|
|
27
|
+
rl.close();
|
|
28
|
+
resolve();
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function main(): Promise<void> {
|
|
34
|
+
// Create a client with your application's name.
|
|
35
|
+
// This name will be shown in Intiface Central.
|
|
36
|
+
const client = new ButtplugClient('Connection Example');
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
// Create a connector to the server. Default port for Intiface Central is 12345.
|
|
40
|
+
const connector = new ButtplugNodeWebsocketClientConnector(
|
|
41
|
+
'ws://127.0.0.1:12345'
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
console.log('Connecting to Intiface Central...');
|
|
45
|
+
await client.connect(connector);
|
|
46
|
+
|
|
47
|
+
// We're connected!
|
|
48
|
+
console.log('Connected! Check Intiface Central for the client name.');
|
|
49
|
+
await waitForEnter('Press Enter to disconnect...');
|
|
50
|
+
|
|
51
|
+
// Disconnect cleanly
|
|
52
|
+
await client.disconnect();
|
|
53
|
+
console.log('Disconnected.');
|
|
54
|
+
} catch (e) {
|
|
55
|
+
if (e instanceof ButtplugClientConnectorException) {
|
|
56
|
+
// Connection failed - server not running, wrong address, network issues, etc.
|
|
57
|
+
console.log(`Can't connect to server: ${e.message}`);
|
|
58
|
+
console.log(
|
|
59
|
+
'Make sure Intiface Central is running and the server is started.'
|
|
60
|
+
);
|
|
61
|
+
} else if (e instanceof ButtplugInitError) {
|
|
62
|
+
// Client/server version mismatch - need to upgrade one or the other
|
|
63
|
+
console.log(`Handshake failed: ${e.message}`);
|
|
64
|
+
console.log('Client and server versions may be incompatible.');
|
|
65
|
+
} else if (e instanceof ButtplugError) {
|
|
66
|
+
// Other Buttplug-specific errors
|
|
67
|
+
console.log(`Buttplug error: ${e.message}`);
|
|
68
|
+
} else {
|
|
69
|
+
throw e;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
await waitForEnter('Press Enter to exit...');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// Buttplug TypeScript - Device Control Example
|
|
2
|
+
//
|
|
3
|
+
// This example demonstrates how to send commands to devices,
|
|
4
|
+
// query device capabilities, and use the command builder API.
|
|
5
|
+
//
|
|
6
|
+
// Prerequisites:
|
|
7
|
+
// 1. Install Intiface Central: https://intiface.com/central
|
|
8
|
+
// 2. Start the server in Intiface Central
|
|
9
|
+
// 3. Run: npx ts-node --esm device-control-example.ts
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
ButtplugClient,
|
|
13
|
+
ButtplugNodeWebsocketClientConnector,
|
|
14
|
+
ButtplugClientDevice,
|
|
15
|
+
DeviceOutput,
|
|
16
|
+
OutputType,
|
|
17
|
+
InputType,
|
|
18
|
+
} from 'buttplug';
|
|
19
|
+
import * as readline from 'readline';
|
|
20
|
+
|
|
21
|
+
async function waitForEnter(prompt: string): Promise<void> {
|
|
22
|
+
const rl = readline.createInterface({
|
|
23
|
+
input: process.stdin,
|
|
24
|
+
output: process.stdout,
|
|
25
|
+
});
|
|
26
|
+
return new Promise((resolve) => {
|
|
27
|
+
rl.question(prompt, () => {
|
|
28
|
+
rl.close();
|
|
29
|
+
resolve();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function delay(ms: number): Promise<void> {
|
|
35
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function main(): Promise<void> {
|
|
39
|
+
const client = new ButtplugClient('Device Control Example');
|
|
40
|
+
|
|
41
|
+
// Connect and scan for devices
|
|
42
|
+
const connector = new ButtplugNodeWebsocketClientConnector(
|
|
43
|
+
'ws://127.0.0.1:12345'
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
console.log('Connecting...');
|
|
47
|
+
await client.connect(connector);
|
|
48
|
+
console.log('Connected! Scanning for devices...');
|
|
49
|
+
|
|
50
|
+
await client.startScanning();
|
|
51
|
+
await waitForEnter('Turn on a device, then press Enter...');
|
|
52
|
+
await client.stopScanning();
|
|
53
|
+
|
|
54
|
+
// Check if we have any devices
|
|
55
|
+
if (client.devices.size === 0) {
|
|
56
|
+
console.log('No devices found. Exiting.');
|
|
57
|
+
await client.disconnect();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Get the first device
|
|
62
|
+
const device: ButtplugClientDevice = client.devices.values().next().value!;
|
|
63
|
+
console.log(`\nUsing device: ${device.name}`);
|
|
64
|
+
|
|
65
|
+
// Show what output types this device supports
|
|
66
|
+
console.log('\nSupported output types:');
|
|
67
|
+
for (const [index, feature] of device.features) {
|
|
68
|
+
const def = (feature as any)._feature;
|
|
69
|
+
if (def.Output) {
|
|
70
|
+
for (const outputType of Object.keys(def.Output)) {
|
|
71
|
+
console.log(
|
|
72
|
+
` - ${outputType} (Feature ${index}: ${def.FeatureDescriptor})`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Check for vibration support and demonstrate commands
|
|
79
|
+
if (device.hasOutput(OutputType.Vibrate)) {
|
|
80
|
+
console.log('\nDevice supports vibration.');
|
|
81
|
+
|
|
82
|
+
// Use the command builder API
|
|
83
|
+
console.log('Vibrating at 50% using command builder...');
|
|
84
|
+
await device.runOutput(DeviceOutput.Vibrate.percent(0.5));
|
|
85
|
+
await delay(1000);
|
|
86
|
+
|
|
87
|
+
console.log('Vibrating at 75%...');
|
|
88
|
+
await device.runOutput(DeviceOutput.Vibrate.percent(0.75));
|
|
89
|
+
await delay(1000);
|
|
90
|
+
|
|
91
|
+
console.log('Vibrating at 25%...');
|
|
92
|
+
await device.runOutput(DeviceOutput.Vibrate.percent(0.25));
|
|
93
|
+
await delay(1000);
|
|
94
|
+
|
|
95
|
+
// Stop the device
|
|
96
|
+
console.log('Stopping device...');
|
|
97
|
+
await device.stop();
|
|
98
|
+
} else {
|
|
99
|
+
console.log('\nDevice does not support vibration.');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Demonstrate other output types if available
|
|
103
|
+
if (device.hasOutput(OutputType.Rotate)) {
|
|
104
|
+
console.log('\nDevice supports rotation. Rotating at 50%...');
|
|
105
|
+
await device.runOutput(DeviceOutput.Rotate.percent(0.5));
|
|
106
|
+
await delay(1000);
|
|
107
|
+
await device.stop();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (device.hasOutput(OutputType.Position)) {
|
|
111
|
+
console.log('\nDevice supports position control. Moving to 100% over 500ms...');
|
|
112
|
+
await device.runOutput(DeviceOutput.PositionWithDuration.percent(1.0, 500));
|
|
113
|
+
await delay(1000);
|
|
114
|
+
await device.runOutput(DeviceOutput.PositionWithDuration.percent(0.0, 500));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Try reading battery level if supported
|
|
118
|
+
if (device.hasInput(InputType.Battery)) {
|
|
119
|
+
console.log('\nReading battery level...');
|
|
120
|
+
const battery = await device.battery();
|
|
121
|
+
console.log(`Battery: ${(battery * 100).toFixed(0)}%`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
await waitForEnter('\nPress Enter to disconnect...');
|
|
125
|
+
|
|
126
|
+
// Disconnect - this automatically stops all devices
|
|
127
|
+
await client.disconnect();
|
|
128
|
+
console.log('Disconnected.');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
main().catch(console.error);
|