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 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;