ive-connect 0.1.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/README.md +251 -0
- package/dist/core/device-interface.d.ts +121 -0
- package/dist/core/device-interface.js +25 -0
- package/dist/core/device-manager.d.ts +74 -0
- package/dist/core/device-manager.js +211 -0
- package/dist/core/events.d.ts +32 -0
- package/dist/core/events.js +70 -0
- package/dist/devices/buttplug/buttplug-api.d.ts +104 -0
- package/dist/devices/buttplug/buttplug-api.js +459 -0
- package/dist/devices/buttplug/buttplug-device.d.ts +85 -0
- package/dist/devices/buttplug/buttplug-device.js +511 -0
- package/dist/devices/buttplug/buttplug-server.d.ts +13 -0
- package/dist/devices/buttplug/buttplug-server.js +27 -0
- package/dist/devices/buttplug/command-helpers.d.ts +23 -0
- package/dist/devices/buttplug/command-helpers.js +95 -0
- package/dist/devices/buttplug/types.d.ts +59 -0
- package/dist/devices/buttplug/types.js +20 -0
- package/dist/devices/handy/handy-api.d.ts +113 -0
- package/dist/devices/handy/handy-api.js +361 -0
- package/dist/devices/handy/handy-device.d.ts +94 -0
- package/dist/devices/handy/handy-device.js +413 -0
- package/dist/devices/handy/types.d.ts +87 -0
- package/dist/devices/handy/types.js +2 -0
- package/dist/devices/index.d.ts +11 -0
- package/dist/devices/index.js +29 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +33 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# ive-connect
|
|
2
|
+
|
|
3
|
+
A universal haptic device control library that provides a consistent interface for managing various haptic devices (Handy, Buttplug, etc.).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Unified device control interface
|
|
8
|
+
- Support for multiple device types
|
|
9
|
+
- Event-based state management
|
|
10
|
+
- TypeScript support
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install ive-connect
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { DeviceManager, HandyDevice } from "ive-connect";
|
|
22
|
+
|
|
23
|
+
// Create a device manager
|
|
24
|
+
const manager = new DeviceManager();
|
|
25
|
+
|
|
26
|
+
// Create and register a Handy device
|
|
27
|
+
const handyDevice = new HandyDevice({
|
|
28
|
+
connectionKey: "your-connection-key",
|
|
29
|
+
});
|
|
30
|
+
manager.registerDevice(handyDevice);
|
|
31
|
+
|
|
32
|
+
// Connect to the device
|
|
33
|
+
await handyDevice.connect();
|
|
34
|
+
|
|
35
|
+
// Load a script
|
|
36
|
+
await handyDevice.loadScript({
|
|
37
|
+
type: "funscript",
|
|
38
|
+
url: "https://example.com/script.funscript",
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Start playback
|
|
42
|
+
await handyDevice.play(0, 1.0, false);
|
|
43
|
+
|
|
44
|
+
// Later, stop playback
|
|
45
|
+
await handyDevice.stop();
|
|
46
|
+
|
|
47
|
+
// Disconnect when done
|
|
48
|
+
await handyDevice.disconnect();
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Supported Devices
|
|
52
|
+
|
|
53
|
+
### Handy
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { HandyDevice } from "ive-connect";
|
|
57
|
+
|
|
58
|
+
const handy = new HandyDevice({
|
|
59
|
+
connectionKey: "your-connection-key",
|
|
60
|
+
// Optional custom configuration
|
|
61
|
+
baseV3Url: "https://www.handyfeeling.com/api/v3",
|
|
62
|
+
baseV2Url: "https://www.handyfeeling.com/api/v2",
|
|
63
|
+
applicationId: "YourAppName",
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Connect to the device
|
|
67
|
+
await handy.connect();
|
|
68
|
+
|
|
69
|
+
// Update configuration
|
|
70
|
+
await handy.updateConfig({
|
|
71
|
+
offset: -200, // Timing offset in milliseconds
|
|
72
|
+
stroke: {
|
|
73
|
+
min: 0.1, // Min stroke position (0.0 to 1.0)
|
|
74
|
+
max: 0.9, // Max stroke position (0.0 to 1.0)
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Listen for events
|
|
79
|
+
handy.on("connected", (deviceInfo) => {
|
|
80
|
+
console.log("Connected to Handy:", deviceInfo);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
handy.on("playbackStateChanged", (state) => {
|
|
84
|
+
console.log("Playback state changed:", state.isPlaying);
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Using the Device Manager
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import { DeviceManager, HandyDevice } from "ive-connect";
|
|
92
|
+
|
|
93
|
+
// Create a device manager
|
|
94
|
+
const manager = new DeviceManager();
|
|
95
|
+
|
|
96
|
+
// Register devices
|
|
97
|
+
const handy = new HandyDevice({
|
|
98
|
+
connectionKey: "your-connection-key",
|
|
99
|
+
});
|
|
100
|
+
manager.registerDevice(handy);
|
|
101
|
+
|
|
102
|
+
// Connect all devices
|
|
103
|
+
await manager.connectAll();
|
|
104
|
+
|
|
105
|
+
// Load a script to all connected devices
|
|
106
|
+
await manager.loadScriptAll({
|
|
107
|
+
type: "funscript",
|
|
108
|
+
url: "https://example.com/script.funscript",
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Start playback on all devices
|
|
112
|
+
await manager.playAll(0, 1.0, false);
|
|
113
|
+
|
|
114
|
+
// Sync time on all devices every second
|
|
115
|
+
setInterval(() => {
|
|
116
|
+
manager.syncTimeAll(videoElement.currentTime * 1000);
|
|
117
|
+
}, 1000);
|
|
118
|
+
|
|
119
|
+
// Listen for events from any device
|
|
120
|
+
manager.on("device:connected", ({ deviceId, data }) => {
|
|
121
|
+
console.log(`Device ${deviceId} connected:`, data);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Listen for events from a specific device
|
|
125
|
+
manager.on(`device:${handy.id}:error`, (error) => {
|
|
126
|
+
console.error(`Error from ${handy.id}:`, error);
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## API Reference
|
|
131
|
+
|
|
132
|
+
### Core Classes
|
|
133
|
+
|
|
134
|
+
#### `DeviceManager`
|
|
135
|
+
|
|
136
|
+
Central manager for all haptic devices.
|
|
137
|
+
|
|
138
|
+
- `registerDevice(device: HapticDevice): void` - Register a device
|
|
139
|
+
- `unregisterDevice(deviceId: string): void` - Unregister a device
|
|
140
|
+
- `getDevices(): HapticDevice[]` - Get all registered devices
|
|
141
|
+
- `getDevice(deviceId: string): HapticDevice | undefined` - Get a specific device
|
|
142
|
+
- `connectAll(): Promise<Record<string, boolean>>` - Connect all devices
|
|
143
|
+
- `disconnectAll(): Promise<Record<string, boolean>>` - Disconnect all devices
|
|
144
|
+
- `loadScriptAll(scriptData: ScriptData): Promise<Record<string, boolean>>` - Load script to all devices
|
|
145
|
+
- `playAll(timeMs: number, playbackRate?: number, loop?: boolean): Promise<Record<string, boolean>>` - Start playback on all devices
|
|
146
|
+
- `stopAll(): Promise<Record<string, boolean>>` - Stop playback on all devices
|
|
147
|
+
- `syncTimeAll(timeMs: number): Promise<Record<string, boolean>>` - Sync time on all playing devices
|
|
148
|
+
|
|
149
|
+
#### `HandyDevice`
|
|
150
|
+
|
|
151
|
+
Implementation of the Handy device.
|
|
152
|
+
|
|
153
|
+
- `connect(config?: Partial<HandySettings>): Promise<boolean>` - Connect to the device
|
|
154
|
+
- `disconnect(): Promise<boolean>` - Disconnect from the device
|
|
155
|
+
- `getConfig(): HandySettings` - Get current configuration
|
|
156
|
+
- `updateConfig(config: Partial<HandySettings>): Promise<boolean>` - Update configuration
|
|
157
|
+
- `loadScript(scriptData: ScriptData): Promise<boolean>` - Load a script
|
|
158
|
+
- `play(timeMs: number, playbackRate?: number, loop?: boolean): Promise<boolean>` - Start playback
|
|
159
|
+
- `stop(): Promise<boolean>` - Stop playback
|
|
160
|
+
- `syncTime(timeMs: number): Promise<boolean>` - Sync time
|
|
161
|
+
- `getDeviceInfo(): DeviceInfo | null` - Get device information
|
|
162
|
+
|
|
163
|
+
### Events
|
|
164
|
+
|
|
165
|
+
Devices and the DeviceManager emit various events that you can listen to:
|
|
166
|
+
|
|
167
|
+
#### Device Events
|
|
168
|
+
|
|
169
|
+
- `error` - Error occurred
|
|
170
|
+
- `connected` - Device connected
|
|
171
|
+
- `disconnected` - Device disconnected
|
|
172
|
+
- `connectionStateChanged` - Connection state changed
|
|
173
|
+
- `playbackStateChanged` - Playback state changed
|
|
174
|
+
- `scriptLoaded` - Script loaded successfully
|
|
175
|
+
- `configChanged` - Configuration changed
|
|
176
|
+
|
|
177
|
+
#### DeviceManager Events
|
|
178
|
+
|
|
179
|
+
- `deviceAdded` - Device was added to the manager
|
|
180
|
+
- `deviceRemoved` - Device was removed from the manager
|
|
181
|
+
- `device:{deviceId}:{eventName}` - Event from a specific device
|
|
182
|
+
- `device:{eventName}` - Event from any device
|
|
183
|
+
|
|
184
|
+
## Interfaces
|
|
185
|
+
|
|
186
|
+
### `HapticDevice`
|
|
187
|
+
|
|
188
|
+
Common interface for all haptic devices.
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
interface HapticDevice {
|
|
192
|
+
readonly id: string;
|
|
193
|
+
readonly name: string;
|
|
194
|
+
readonly type: string;
|
|
195
|
+
readonly capabilities: DeviceCapability[];
|
|
196
|
+
readonly isConnected: boolean;
|
|
197
|
+
readonly isPlaying: boolean;
|
|
198
|
+
|
|
199
|
+
connect(config?: any): Promise<boolean>;
|
|
200
|
+
disconnect(): Promise<boolean>;
|
|
201
|
+
getConfig(): DeviceSettings;
|
|
202
|
+
updateConfig(config: Partial<DeviceSettings>): Promise<boolean>;
|
|
203
|
+
loadScript(scriptData: ScriptData): Promise<boolean>;
|
|
204
|
+
play(timeMs: number, playbackRate?: number, loop?: boolean): Promise<boolean>;
|
|
205
|
+
stop(): Promise<boolean>;
|
|
206
|
+
syncTime(timeMs: number): Promise<boolean>;
|
|
207
|
+
getDeviceInfo(): DeviceInfo | null;
|
|
208
|
+
on(event: string, callback: (data: any) => void): void;
|
|
209
|
+
off(event: string, callback: (data: any) => void): void;
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### `ScriptData`
|
|
214
|
+
|
|
215
|
+
Data for scripts to be loaded.
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
interface ScriptData {
|
|
219
|
+
type: string; // Script type (e.g., "funscript")
|
|
220
|
+
url?: string; // URL to script if remote
|
|
221
|
+
content?: any; // Script content if loaded directly
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### `DeviceSettings`
|
|
226
|
+
|
|
227
|
+
Base interface for device settings.
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
interface DeviceSettings {
|
|
231
|
+
id: string; // Device identifier
|
|
232
|
+
name: string; // Human-readable name
|
|
233
|
+
enabled: boolean; // Whether the device is enabled
|
|
234
|
+
[key: string]: any; // Additional device-specific settings
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### `HandySettings`
|
|
239
|
+
|
|
240
|
+
Handy-specific settings.
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
interface HandySettings extends DeviceSettings {
|
|
244
|
+
connectionKey: string; // Device connection key
|
|
245
|
+
offset: number; // Timing offset in milliseconds
|
|
246
|
+
stroke: {
|
|
247
|
+
min: number; // Min stroke position (0.0 to 1.0)
|
|
248
|
+
max: number; // Max stroke position (0.0 to 1.0)
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
```
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core interfaces for haptic devices
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The connection state of a device
|
|
6
|
+
*/
|
|
7
|
+
export declare enum ConnectionState {
|
|
8
|
+
DISCONNECTED = "disconnected",
|
|
9
|
+
CONNECTING = "connecting",
|
|
10
|
+
CONNECTED = "connected"
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Common device capabilities
|
|
14
|
+
*/
|
|
15
|
+
export declare enum DeviceCapability {
|
|
16
|
+
VIBRATE = "vibrate",
|
|
17
|
+
ROTATE = "rotate",
|
|
18
|
+
LINEAR = "linear",
|
|
19
|
+
STROKE = "stroke"
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Generic device information
|
|
23
|
+
*/
|
|
24
|
+
export interface DeviceInfo {
|
|
25
|
+
id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
type: string;
|
|
28
|
+
firmware?: string;
|
|
29
|
+
hardware?: string;
|
|
30
|
+
[key: string]: any;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Device settings interface
|
|
34
|
+
* Base interface for device-specific settings
|
|
35
|
+
*/
|
|
36
|
+
export interface DeviceSettings {
|
|
37
|
+
id: string;
|
|
38
|
+
name: string;
|
|
39
|
+
enabled: boolean;
|
|
40
|
+
[key: string]: any;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Script data interface
|
|
44
|
+
*/
|
|
45
|
+
export interface ScriptData {
|
|
46
|
+
type: string;
|
|
47
|
+
url?: string;
|
|
48
|
+
content?: any;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Common interface for all haptic devices
|
|
52
|
+
*/
|
|
53
|
+
export interface HapticDevice {
|
|
54
|
+
/**
|
|
55
|
+
* Device information
|
|
56
|
+
*/
|
|
57
|
+
readonly id: string;
|
|
58
|
+
readonly name: string;
|
|
59
|
+
readonly type: string;
|
|
60
|
+
readonly capabilities: DeviceCapability[];
|
|
61
|
+
/**
|
|
62
|
+
* Connection state
|
|
63
|
+
*/
|
|
64
|
+
readonly isConnected: boolean;
|
|
65
|
+
readonly isPlaying: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Connect to the device
|
|
68
|
+
* @param config Optional configuration
|
|
69
|
+
*/
|
|
70
|
+
connect(config?: any): Promise<boolean>;
|
|
71
|
+
/**
|
|
72
|
+
* Disconnect from the device
|
|
73
|
+
*/
|
|
74
|
+
disconnect(): Promise<boolean>;
|
|
75
|
+
/**
|
|
76
|
+
* Get current device configuration
|
|
77
|
+
*/
|
|
78
|
+
getConfig(): DeviceSettings;
|
|
79
|
+
/**
|
|
80
|
+
* Update device configuration
|
|
81
|
+
* @param config Partial configuration to update
|
|
82
|
+
*/
|
|
83
|
+
updateConfig(config: Partial<DeviceSettings>): Promise<boolean>;
|
|
84
|
+
/**
|
|
85
|
+
* Load a script for playback
|
|
86
|
+
* @param scriptData Script data to load
|
|
87
|
+
*/
|
|
88
|
+
loadScript(scriptData: ScriptData): Promise<boolean>;
|
|
89
|
+
/**
|
|
90
|
+
* Play the loaded script at the specified time
|
|
91
|
+
* @param timeMs Current time in milliseconds
|
|
92
|
+
* @param playbackRate Playback rate (1.0 = normal speed)
|
|
93
|
+
* @param loop Whether to loop the script
|
|
94
|
+
*/
|
|
95
|
+
play(timeMs: number, playbackRate?: number, loop?: boolean): Promise<boolean>;
|
|
96
|
+
/**
|
|
97
|
+
* Stop playback
|
|
98
|
+
*/
|
|
99
|
+
stop(): Promise<boolean>;
|
|
100
|
+
/**
|
|
101
|
+
* Synchronize device time with provided time
|
|
102
|
+
* @param timeMs Current time in milliseconds
|
|
103
|
+
*/
|
|
104
|
+
syncTime(timeMs: number): Promise<boolean>;
|
|
105
|
+
/**
|
|
106
|
+
* Get device-specific information
|
|
107
|
+
*/
|
|
108
|
+
getDeviceInfo(): DeviceInfo | null;
|
|
109
|
+
/**
|
|
110
|
+
* Add event listener
|
|
111
|
+
* @param event Event name
|
|
112
|
+
* @param callback Callback function
|
|
113
|
+
*/
|
|
114
|
+
on(event: string, callback: (data: any) => void): void;
|
|
115
|
+
/**
|
|
116
|
+
* Remove event listener
|
|
117
|
+
* @param event Event name
|
|
118
|
+
* @param callback Callback function
|
|
119
|
+
*/
|
|
120
|
+
off(event: string, callback: (data: any) => void): void;
|
|
121
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Core interfaces for haptic devices
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DeviceCapability = exports.ConnectionState = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* The connection state of a device
|
|
9
|
+
*/
|
|
10
|
+
var ConnectionState;
|
|
11
|
+
(function (ConnectionState) {
|
|
12
|
+
ConnectionState["DISCONNECTED"] = "disconnected";
|
|
13
|
+
ConnectionState["CONNECTING"] = "connecting";
|
|
14
|
+
ConnectionState["CONNECTED"] = "connected";
|
|
15
|
+
})(ConnectionState || (exports.ConnectionState = ConnectionState = {}));
|
|
16
|
+
/**
|
|
17
|
+
* Common device capabilities
|
|
18
|
+
*/
|
|
19
|
+
var DeviceCapability;
|
|
20
|
+
(function (DeviceCapability) {
|
|
21
|
+
DeviceCapability["VIBRATE"] = "vibrate";
|
|
22
|
+
DeviceCapability["ROTATE"] = "rotate";
|
|
23
|
+
DeviceCapability["LINEAR"] = "linear";
|
|
24
|
+
DeviceCapability["STROKE"] = "stroke";
|
|
25
|
+
})(DeviceCapability || (exports.DeviceCapability = DeviceCapability = {}));
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device Manager
|
|
3
|
+
*
|
|
4
|
+
* Central manager for all haptic devices
|
|
5
|
+
*/
|
|
6
|
+
import { EventEmitter } from "./events";
|
|
7
|
+
import { HapticDevice, ScriptData } from "./device-interface";
|
|
8
|
+
/**
|
|
9
|
+
* Device Manager class
|
|
10
|
+
* Handles registration and control of multiple haptic devices
|
|
11
|
+
*/
|
|
12
|
+
export declare class DeviceManager extends EventEmitter {
|
|
13
|
+
private devices;
|
|
14
|
+
private scriptData;
|
|
15
|
+
/**
|
|
16
|
+
* Register a device with the manager
|
|
17
|
+
* @param device Device to register
|
|
18
|
+
*/
|
|
19
|
+
registerDevice(device: HapticDevice): void;
|
|
20
|
+
/**
|
|
21
|
+
* Unregister a device from the manager
|
|
22
|
+
* @param deviceId Device ID to unregister
|
|
23
|
+
*/
|
|
24
|
+
unregisterDevice(deviceId: string): void;
|
|
25
|
+
/**
|
|
26
|
+
* Get all registered devices
|
|
27
|
+
*/
|
|
28
|
+
getDevices(): HapticDevice[];
|
|
29
|
+
/**
|
|
30
|
+
* Get a specific device by ID
|
|
31
|
+
* @param deviceId Device ID to retrieve
|
|
32
|
+
*/
|
|
33
|
+
getDevice(deviceId: string): HapticDevice | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Connect to all registered devices
|
|
36
|
+
* @returns Object with success status for each device
|
|
37
|
+
*/
|
|
38
|
+
connectAll(): Promise<Record<string, boolean>>;
|
|
39
|
+
/**
|
|
40
|
+
* Disconnect from all registered devices
|
|
41
|
+
* @returns Object with success status for each device
|
|
42
|
+
*/
|
|
43
|
+
disconnectAll(): Promise<Record<string, boolean>>;
|
|
44
|
+
/**
|
|
45
|
+
* Load a script to all connected devices
|
|
46
|
+
* @param scriptData Script data to load
|
|
47
|
+
* @returns Object with success status for each device
|
|
48
|
+
*/
|
|
49
|
+
loadScriptAll(scriptData: ScriptData): Promise<Record<string, boolean>>;
|
|
50
|
+
/**
|
|
51
|
+
* Start playback on all connected devices
|
|
52
|
+
* @param timeMs Current time in milliseconds
|
|
53
|
+
* @param playbackRate Playback rate (1.0 = normal speed)
|
|
54
|
+
* @param loop Whether to loop the script
|
|
55
|
+
* @returns Object with success status for each device
|
|
56
|
+
*/
|
|
57
|
+
playAll(timeMs: number, playbackRate?: number, loop?: boolean): Promise<Record<string, boolean>>;
|
|
58
|
+
/**
|
|
59
|
+
* Stop playback on all connected devices
|
|
60
|
+
* @returns Object with success status for each device
|
|
61
|
+
*/
|
|
62
|
+
stopAll(): Promise<Record<string, boolean>>;
|
|
63
|
+
/**
|
|
64
|
+
* Synchronize time on all connected and playing devices
|
|
65
|
+
* @param timeMs Current time in milliseconds
|
|
66
|
+
* @returns Object with success status for each device
|
|
67
|
+
*/
|
|
68
|
+
syncTimeAll(timeMs: number): Promise<Record<string, boolean>>;
|
|
69
|
+
/**
|
|
70
|
+
* Set up event forwarding from a device to the manager
|
|
71
|
+
* @param device Device to forward events from
|
|
72
|
+
*/
|
|
73
|
+
private setupDeviceEventForwarding;
|
|
74
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DeviceManager = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Device Manager
|
|
6
|
+
*
|
|
7
|
+
* Central manager for all haptic devices
|
|
8
|
+
*/
|
|
9
|
+
const events_1 = require("./events");
|
|
10
|
+
/**
|
|
11
|
+
* Device Manager class
|
|
12
|
+
* Handles registration and control of multiple haptic devices
|
|
13
|
+
*/
|
|
14
|
+
class DeviceManager extends events_1.EventEmitter {
|
|
15
|
+
constructor() {
|
|
16
|
+
super(...arguments);
|
|
17
|
+
this.devices = new Map();
|
|
18
|
+
this.scriptData = null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Register a device with the manager
|
|
22
|
+
* @param device Device to register
|
|
23
|
+
*/
|
|
24
|
+
registerDevice(device) {
|
|
25
|
+
// Don't register the same device twice
|
|
26
|
+
if (this.devices.has(device.id)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
this.devices.set(device.id, device);
|
|
30
|
+
// Forward events from this device
|
|
31
|
+
this.setupDeviceEventForwarding(device);
|
|
32
|
+
// Emit device added event
|
|
33
|
+
this.emit("deviceAdded", device);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Unregister a device from the manager
|
|
37
|
+
* @param deviceId Device ID to unregister
|
|
38
|
+
*/
|
|
39
|
+
unregisterDevice(deviceId) {
|
|
40
|
+
const device = this.devices.get(deviceId);
|
|
41
|
+
if (device) {
|
|
42
|
+
this.devices.delete(deviceId);
|
|
43
|
+
this.emit("deviceRemoved", device);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get all registered devices
|
|
48
|
+
*/
|
|
49
|
+
getDevices() {
|
|
50
|
+
return Array.from(this.devices.values());
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get a specific device by ID
|
|
54
|
+
* @param deviceId Device ID to retrieve
|
|
55
|
+
*/
|
|
56
|
+
getDevice(deviceId) {
|
|
57
|
+
return this.devices.get(deviceId);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Connect to all registered devices
|
|
61
|
+
* @returns Object with success status for each device
|
|
62
|
+
*/
|
|
63
|
+
async connectAll() {
|
|
64
|
+
const results = {};
|
|
65
|
+
for (const [id, device] of this.devices.entries()) {
|
|
66
|
+
try {
|
|
67
|
+
results[id] = await device.connect();
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
console.error(`Error connecting device ${id}:`, error);
|
|
71
|
+
results[id] = false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return results;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Disconnect from all registered devices
|
|
78
|
+
* @returns Object with success status for each device
|
|
79
|
+
*/
|
|
80
|
+
async disconnectAll() {
|
|
81
|
+
const results = {};
|
|
82
|
+
for (const [id, device] of this.devices.entries()) {
|
|
83
|
+
try {
|
|
84
|
+
results[id] = await device.disconnect();
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
console.error(`Error disconnecting device ${id}:`, error);
|
|
88
|
+
results[id] = false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return results;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Load a script to all connected devices
|
|
95
|
+
* @param scriptData Script data to load
|
|
96
|
+
* @returns Object with success status for each device
|
|
97
|
+
*/
|
|
98
|
+
async loadScriptAll(scriptData) {
|
|
99
|
+
const results = {};
|
|
100
|
+
this.scriptData = scriptData;
|
|
101
|
+
for (const [id, device] of this.devices.entries()) {
|
|
102
|
+
if (device.isConnected) {
|
|
103
|
+
try {
|
|
104
|
+
results[id] = await device.loadScript(scriptData);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
console.error(`Error loading script to device ${id}:`, error);
|
|
108
|
+
results[id] = false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
results[id] = false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return results;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Start playback on all connected devices
|
|
119
|
+
* @param timeMs Current time in milliseconds
|
|
120
|
+
* @param playbackRate Playback rate (1.0 = normal speed)
|
|
121
|
+
* @param loop Whether to loop the script
|
|
122
|
+
* @returns Object with success status for each device
|
|
123
|
+
*/
|
|
124
|
+
async playAll(timeMs, playbackRate = 1.0, loop = false) {
|
|
125
|
+
const results = {};
|
|
126
|
+
for (const [id, device] of this.devices.entries()) {
|
|
127
|
+
if (device.isConnected) {
|
|
128
|
+
try {
|
|
129
|
+
results[id] = await device.play(timeMs, playbackRate, loop);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
console.error(`Error playing on device ${id}:`, error);
|
|
133
|
+
results[id] = false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
results[id] = false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return results;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Stop playback on all connected devices
|
|
144
|
+
* @returns Object with success status for each device
|
|
145
|
+
*/
|
|
146
|
+
async stopAll() {
|
|
147
|
+
const results = {};
|
|
148
|
+
for (const [id, device] of this.devices.entries()) {
|
|
149
|
+
if (device.isConnected) {
|
|
150
|
+
try {
|
|
151
|
+
results[id] = await device.stop();
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
console.error(`Error stopping device ${id}:`, error);
|
|
155
|
+
results[id] = false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
results[id] = false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return results;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Synchronize time on all connected and playing devices
|
|
166
|
+
* @param timeMs Current time in milliseconds
|
|
167
|
+
* @returns Object with success status for each device
|
|
168
|
+
*/
|
|
169
|
+
async syncTimeAll(timeMs) {
|
|
170
|
+
const results = {};
|
|
171
|
+
for (const [id, device] of this.devices.entries()) {
|
|
172
|
+
if (device.isConnected && device.isPlaying) {
|
|
173
|
+
try {
|
|
174
|
+
results[id] = await device.syncTime(timeMs);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
console.error(`Error syncing time on device ${id}:`, error);
|
|
178
|
+
results[id] = false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
results[id] = false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return results;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Set up event forwarding from a device to the manager
|
|
189
|
+
* @param device Device to forward events from
|
|
190
|
+
*/
|
|
191
|
+
setupDeviceEventForwarding(device) {
|
|
192
|
+
// Forward common events
|
|
193
|
+
const eventsToForward = [
|
|
194
|
+
"error",
|
|
195
|
+
"connected",
|
|
196
|
+
"disconnected",
|
|
197
|
+
"connectionStateChanged",
|
|
198
|
+
"playbackStateChanged",
|
|
199
|
+
"scriptLoaded",
|
|
200
|
+
"configChanged",
|
|
201
|
+
];
|
|
202
|
+
for (const eventName of eventsToForward) {
|
|
203
|
+
device.on(eventName, (data) => {
|
|
204
|
+
this.emit(`device:${device.id}:${eventName}`, data);
|
|
205
|
+
// Also emit a general event for any device
|
|
206
|
+
this.emit(`device:${eventName}`, { deviceId: device.id, data });
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
exports.DeviceManager = DeviceManager;
|