@@ -9,7 +9,11 @@ import type {
9
9
State ,
10
10
} from "@ai16z/eliza" ;
11
11
import { Service , ServiceType } from "@ai16z/eliza" ;
12
- import { isPortAvailable , startIntifaceEngine } from "./utils" ;
12
+ import {
13
+ isPortAvailable ,
14
+ startIntifaceEngine ,
15
+ shutdownIntifaceEngine ,
16
+ } from "./utils" ;
13
17
14
18
export interface IButtplugService extends Service {
15
19
vibrate ( strength : number , duration : number ) : Promise < void > ;
@@ -44,6 +48,22 @@ export class ButtplugService extends Service implements IButtplugService {
44
48
"deviceremoved" ,
45
49
this . handleDeviceRemoved . bind ( this )
46
50
) ;
51
+
52
+ // Add cleanup handlers
53
+ process . on ( "SIGINT" , this . cleanup . bind ( this ) ) ;
54
+ process . on ( "SIGTERM" , this . cleanup . bind ( this ) ) ;
55
+ process . on ( "exit" , this . cleanup . bind ( this ) ) ;
56
+ }
57
+
58
+ private async cleanup ( ) {
59
+ try {
60
+ if ( this . connected ) {
61
+ await this . client . disconnect ( ) ;
62
+ }
63
+ await shutdownIntifaceEngine ( ) ;
64
+ } catch ( error ) {
65
+ console . error ( "[ButtplugService] Cleanup error:" , error ) ;
66
+ }
47
67
}
48
68
49
69
getInstance ( ) : IButtplugService {
@@ -68,7 +88,6 @@ export class ButtplugService extends Service implements IButtplugService {
68
88
if ( portAvailable ) {
69
89
try {
70
90
await startIntifaceEngine ( ) ;
71
- await new Promise ( ( resolve ) => setTimeout ( resolve , 1000 ) ) ;
72
91
} catch ( error ) {
73
92
console . error ( "Failed to start Intiface Engine:" , error ) ;
74
93
throw error ;
@@ -79,32 +98,78 @@ export class ButtplugService extends Service implements IButtplugService {
79
98
) ;
80
99
}
81
100
82
- const connector = new ButtplugNodeWebsocketClientConnector (
83
- this . config . INTIFACE_URL
84
- ) ;
101
+ let retries = 5 ;
102
+ while ( retries > 0 ) {
103
+ try {
104
+ const connector = new ButtplugNodeWebsocketClientConnector (
105
+ this . config . INTIFACE_URL
106
+ ) ;
85
107
86
- try {
87
- await this . client . connect ( connector ) ;
88
- this . connected = true ;
89
- await this . client . startScanning ( ) ;
90
-
91
- // Wait for device discovery
92
- await new Promise ( ( r ) => setTimeout ( r , 5000 ) ) ;
93
- console . log ( "Scanning for devices..." ) ;
94
-
95
- // Store discovered devices in the map
96
- this . client . devices . forEach ( ( device ) => {
97
- this . devices . set ( device . name , device ) ;
98
- console . log ( `- ${ device . name } (${ device . index } )` ) ;
99
- } ) ;
108
+ await this . client . connect ( connector ) ;
109
+ this . connected = true ;
110
+ await this . scanAndGrabDevices ( ) ;
111
+ return ;
112
+ } catch ( error ) {
113
+ retries -- ;
114
+ if ( retries > 0 ) {
115
+ console . log (
116
+ `Connection attempt failed, retrying... (${ retries } attempts left)`
117
+ ) ;
118
+ await new Promise ( ( r ) => setTimeout ( r , 2000 ) ) ;
119
+ } else {
120
+ console . error (
121
+ "Failed to connect to Buttplug server after all retries:" ,
122
+ error
123
+ ) ;
124
+ throw error ;
125
+ }
126
+ }
127
+ }
128
+ }
129
+
130
+ private async scanAndGrabDevices ( ) {
131
+ await this . client . startScanning ( ) ;
132
+ console . log ( "Scanning for devices..." ) ;
133
+ await new Promise ( ( r ) => setTimeout ( r , 2000 ) ) ;
134
+
135
+ this . client . devices . forEach ( ( device ) => {
136
+ this . devices . set ( device . name , device ) ;
137
+ console . log ( `- ${ device . name } (${ device . index } )` ) ;
138
+ } ) ;
139
+
140
+ if ( this . devices . size === 0 ) {
141
+ console . log ( "No devices found" ) ;
142
+ }
143
+ }
144
+
145
+ private async ensureDeviceAvailable ( ) {
146
+ if ( ! this . connected ) {
147
+ throw new Error ( "Not connected to Buttplug server" ) ;
148
+ }
149
+
150
+ if ( this . devices . size === 0 ) {
151
+ await this . scanAndGrabDevices ( ) ;
152
+ }
100
153
101
- if ( this . devices . size === 0 ) {
102
- console . log ( "No devices found" ) ;
154
+ const devices = this . getDevices ( ) ;
155
+ if ( devices . length === 0 ) {
156
+ throw new Error ( "No devices available" ) ;
157
+ }
158
+
159
+ let targetDevice ;
160
+ if ( this . preferredDeviceName ) {
161
+ targetDevice = this . devices . get ( this . preferredDeviceName ) ;
162
+ if ( ! targetDevice ) {
163
+ console . warn (
164
+ `Preferred device ${ this . preferredDeviceName } not found, using first available device`
165
+ ) ;
166
+ targetDevice = devices [ 0 ] ;
103
167
}
104
- } catch ( error ) {
105
- console . error ( "Failed to connect to Buttplug server:" , error ) ;
106
- throw error ;
168
+ } else {
169
+ targetDevice = devices [ 0 ] ;
107
170
}
171
+
172
+ return targetDevice ;
108
173
}
109
174
110
175
async disconnect ( ) {
@@ -150,27 +215,7 @@ export class ButtplugService extends Service implements IButtplugService {
150
215
}
151
216
152
217
private async handleVibrate ( event : VibrateEvent ) {
153
- if ( ! this . connected ) {
154
- throw new Error ( "Not connected to Buttplug server" ) ;
155
- }
156
-
157
- const devices = this . getDevices ( ) ;
158
- if ( devices . length === 0 ) {
159
- throw new Error ( "No devices available" ) ;
160
- }
161
-
162
- let targetDevice ;
163
- if ( this . preferredDeviceName ) {
164
- targetDevice = this . devices . get ( this . preferredDeviceName ) ;
165
- if ( ! targetDevice ) {
166
- console . warn (
167
- `Preferred device ${ this . preferredDeviceName } not found, using first available device`
168
- ) ;
169
- targetDevice = devices [ 0 ] ;
170
- }
171
- } else {
172
- targetDevice = devices [ 0 ] ;
173
- }
218
+ const targetDevice = await this . ensureDeviceAvailable ( ) ;
174
219
175
220
if ( this . rampUpAndDown ) {
176
221
const steps = this . rampSteps ;
@@ -206,56 +251,16 @@ export class ButtplugService extends Service implements IButtplugService {
206
251
}
207
252
208
253
async vibrate ( strength : number , duration : number ) : Promise < void > {
209
- if ( this . preferredDeviceName ) {
210
- const device = this . devices . get ( this . preferredDeviceName ) ;
211
- if ( ! device ) {
212
- console . log (
213
- `Preferred device ${ this . preferredDeviceName } not found, using first available device`
214
- ) ;
215
- const devices = this . getDevices ( ) ;
216
- if ( devices . length > 0 ) {
217
- await this . addToVibrateQueue ( {
218
- strength,
219
- duration,
220
- deviceId : devices [ 0 ] . id ,
221
- } ) ;
222
- } else {
223
- throw new Error ( "No devices available" ) ;
224
- }
225
- } else {
226
- await this . addToVibrateQueue ( {
227
- strength,
228
- duration,
229
- deviceId : device . id ,
230
- } ) ;
231
- }
232
- } else {
233
- await this . addToVibrateQueue ( { strength, duration } ) ;
234
- }
254
+ const targetDevice = await this . ensureDeviceAvailable ( ) ;
255
+ await this . addToVibrateQueue ( {
256
+ strength,
257
+ duration,
258
+ deviceId : targetDevice . id ,
259
+ } ) ;
235
260
}
236
261
237
262
async getBatteryLevel ( ) : Promise < number > {
238
- if ( ! this . connected ) {
239
- throw new Error ( "Not connected to Buttplug server" ) ;
240
- }
241
-
242
- const devices = this . getDevices ( ) ;
243
- if ( devices . length === 0 ) {
244
- throw new Error ( "No devices available" ) ;
245
- }
246
-
247
- let targetDevice ;
248
- if ( this . preferredDeviceName ) {
249
- targetDevice = this . devices . get ( this . preferredDeviceName ) ;
250
- if ( ! targetDevice ) {
251
- console . warn (
252
- `Preferred device ${ this . preferredDeviceName } not found, using first available device`
253
- ) ;
254
- targetDevice = devices [ 0 ] ;
255
- }
256
- } else {
257
- targetDevice = devices [ 0 ] ;
258
- }
263
+ const targetDevice = await this . ensureDeviceAvailable ( ) ;
259
264
260
265
try {
261
266
const battery = await targetDevice . battery ( ) ;
@@ -270,27 +275,7 @@ export class ButtplugService extends Service implements IButtplugService {
270
275
}
271
276
272
277
async rotate ( strength : number , duration : number ) : Promise < void > {
273
- if ( ! this . connected ) {
274
- throw new Error ( "Not connected to Buttplug server" ) ;
275
- }
276
-
277
- const devices = this . getDevices ( ) ;
278
- if ( devices . length === 0 ) {
279
- throw new Error ( "No devices available" ) ;
280
- }
281
-
282
- let targetDevice ;
283
- if ( this . preferredDeviceName ) {
284
- targetDevice = this . devices . get ( this . preferredDeviceName ) ;
285
- if ( ! targetDevice ) {
286
- console . warn (
287
- `Preferred device ${ this . preferredDeviceName } not found, using first available device`
288
- ) ;
289
- targetDevice = devices [ 0 ] ;
290
- }
291
- } else {
292
- targetDevice = devices [ 0 ] ;
293
- }
278
+ const targetDevice = await this . ensureDeviceAvailable ( ) ;
294
279
295
280
// Check if device supports rotation
296
281
if ( ! targetDevice . rotateCmd ) {
0 commit comments