buttplug 3.2.1 → 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.
Files changed (179) hide show
  1. package/.eslintrc.js +25 -25
  2. package/.jscsrc +2 -2
  3. package/.jshintrc +5 -5
  4. package/.prettierrc.json +3 -3
  5. package/.yarnrc.yml +5 -1
  6. package/CHANGELOG.md +577 -571
  7. package/LICENSE +27 -27
  8. package/README.md +97 -97
  9. package/dist/main/src/client/ButtplugBrowserWebsocketClientConnector.js +6 -9
  10. package/dist/main/src/client/ButtplugBrowserWebsocketClientConnector.js.map +1 -1
  11. package/dist/main/src/client/{Client.d.ts → ButtplugClient.d.ts} +3 -4
  12. package/dist/main/src/client/ButtplugClient.js +227 -0
  13. package/dist/main/src/client/ButtplugClient.js.map +1 -0
  14. package/dist/main/src/client/ButtplugClientConnectorException.js +17 -7
  15. package/dist/main/src/client/ButtplugClientConnectorException.js.map +1 -1
  16. package/dist/main/src/client/ButtplugClientDevice.d.ts +13 -28
  17. package/dist/main/src/client/ButtplugClientDevice.js +105 -247
  18. package/dist/main/src/client/ButtplugClientDevice.js.map +1 -1
  19. package/dist/main/src/client/ButtplugClientDeviceCommand.d.ts +42 -0
  20. package/dist/main/src/client/ButtplugClientDeviceCommand.js +105 -0
  21. package/dist/main/src/client/ButtplugClientDeviceCommand.js.map +1 -0
  22. package/dist/main/src/client/ButtplugClientDeviceFeature.d.ts +18 -0
  23. package/dist/main/src/client/ButtplugClientDeviceFeature.js +166 -0
  24. package/dist/main/src/client/ButtplugClientDeviceFeature.js.map +1 -0
  25. package/dist/main/src/client/ButtplugNodeWebsocketClientConnector.d.ts +1 -8
  26. package/dist/main/src/client/ButtplugNodeWebsocketClientConnector.js +1 -4
  27. package/dist/main/src/client/ButtplugNodeWebsocketClientConnector.js.map +1 -1
  28. package/dist/main/src/core/Exceptions.js +27 -9
  29. package/dist/main/src/core/Exceptions.js.map +1 -1
  30. package/dist/main/src/core/Logging.js +12 -6
  31. package/dist/main/src/core/Logging.js.map +1 -1
  32. package/dist/main/src/core/Messages.d.ts +118 -228
  33. package/dist/main/src/core/Messages.js +51 -404
  34. package/dist/main/src/core/Messages.js.map +1 -1
  35. package/dist/main/src/index.d.ts +2 -2
  36. package/dist/main/src/index.js +4 -2
  37. package/dist/main/src/index.js.map +1 -1
  38. package/dist/main/src/utils/ButtplugBrowserWebsocketConnector.js +40 -52
  39. package/dist/main/src/utils/ButtplugBrowserWebsocketConnector.js.map +1 -1
  40. package/dist/main/src/utils/ButtplugMessageSorter.js +27 -15
  41. package/dist/main/src/utils/ButtplugMessageSorter.js.map +1 -1
  42. package/dist/main/src/utils/Utils.js +1 -2
  43. package/dist/main/src/utils/Utils.js.map +1 -1
  44. package/dist/web/buttplug.js +1 -38
  45. package/dist/web/buttplug.mjs +595 -1984
  46. package/dist/web/client/ButtplugBrowserWebsocketClientConnector.d.ts +0 -7
  47. package/dist/web/client/{Client.d.ts → ButtplugClient.d.ts} +3 -11
  48. package/dist/web/client/ButtplugClientConnectorException.d.ts +0 -7
  49. package/dist/web/client/ButtplugClientDevice.d.ts +14 -29
  50. package/dist/web/client/ButtplugClientDeviceCommand.d.ts +42 -0
  51. package/dist/web/client/ButtplugClientDeviceFeature.d.ts +18 -0
  52. package/dist/web/client/ButtplugNodeWebsocketClientConnector.d.ts +1 -15
  53. package/dist/web/client/IButtplugClientConnector.d.ts +0 -7
  54. package/dist/web/core/Exceptions.d.ts +1 -1
  55. package/dist/web/core/Logging.d.ts +0 -7
  56. package/dist/web/core/Messages.d.ts +118 -227
  57. package/dist/web/index.d.ts +2 -2
  58. package/dist/web/utils/ButtplugBrowserWebsocketConnector.d.ts +0 -7
  59. package/examples/node/SYNC_MANIFEST.md +105 -0
  60. package/examples/node/application-example.ts +213 -0
  61. package/examples/node/async-example.ts +124 -0
  62. package/examples/node/connection-example.ts +76 -0
  63. package/examples/node/device-control-example.ts +131 -0
  64. package/examples/node/device-enumeration-example.ts +86 -0
  65. package/examples/node/device-info-example.ts +131 -0
  66. package/examples/node/errors-example.ts +166 -0
  67. package/examples/node/package-lock.json +281 -0
  68. package/examples/node/package.json +25 -0
  69. package/examples/node/remote-connector-example.ts +84 -0
  70. package/examples/node/tsconfig.json +14 -0
  71. package/examples/web/application-example.js +197 -0
  72. package/examples/web/async-example.js +90 -0
  73. package/examples/web/device-control-example.js +87 -0
  74. package/examples/web/device-enumeration-example.js +49 -0
  75. package/examples/web/device-info-example.js +100 -0
  76. package/examples/web/errors-example.js +110 -0
  77. package/examples/web/index.html +55 -0
  78. package/examples/web/logging.js +42 -0
  79. package/examples/web/ping-timeout-example.js +59 -0
  80. package/examples/web/remote-connector-example.js +68 -0
  81. package/node-test.js +24 -0
  82. package/node-test.ts +23 -5
  83. package/package.json +85 -87
  84. package/src/client/ButtplugBrowserWebsocketClientConnector.ts +25 -25
  85. package/src/client/ButtplugClient.ts +242 -0
  86. package/src/client/ButtplugClientConnectorException.ts +16 -16
  87. package/src/client/ButtplugClientDevice.ts +178 -401
  88. package/src/client/ButtplugClientDeviceCommand.ts +112 -0
  89. package/src/client/ButtplugClientDeviceFeature.ts +138 -0
  90. package/src/client/ButtplugNodeWebsocketClientConnector.ts +17 -17
  91. package/src/client/IButtplugClientConnector.ts +18 -18
  92. package/src/core/Exceptions.ts +107 -101
  93. package/src/core/Logging.ts +197 -197
  94. package/src/core/Messages.ts +209 -480
  95. package/src/core/index.d.ts +4 -4
  96. package/src/index.ts +21 -19
  97. package/src/utils/ButtplugBrowserWebsocketConnector.ts +89 -92
  98. package/src/utils/ButtplugMessageSorter.ts +66 -65
  99. package/src/utils/Utils.ts +3 -3
  100. package/tsconfig.json +22 -22
  101. package/tsfmt.json +14 -14
  102. package/tslint.json +27 -27
  103. package/typedocconfig.js +6 -6
  104. package/vite.config.ts +26 -26
  105. package/dist/main/src/client/Client.js +0 -242
  106. package/dist/main/src/client/Client.js.map +0 -1
  107. package/dist/main/src/core/MessageUtils.d.ts +0 -10
  108. package/dist/main/src/core/MessageUtils.js +0 -65
  109. package/dist/main/src/core/MessageUtils.js.map +0 -1
  110. package/dist/web/core/MessageUtils.d.ts +0 -10
  111. package/doc/.nojekyll +0 -1
  112. package/doc/assets/highlight.css +0 -22
  113. package/doc/assets/main.js +0 -58
  114. package/doc/assets/search.js +0 -1
  115. package/doc/assets/style.css +0 -1280
  116. package/doc/classes/ButtplugBrowserWebsocketClientConnector.html +0 -234
  117. package/doc/classes/ButtplugClient.html +0 -331
  118. package/doc/classes/ButtplugClientConnectorException.html +0 -216
  119. package/doc/classes/ButtplugClientDevice.html +0 -489
  120. package/doc/classes/ButtplugDeviceError.html +0 -218
  121. package/doc/classes/ButtplugDeviceMessage.html +0 -165
  122. package/doc/classes/ButtplugError.html +0 -220
  123. package/doc/classes/ButtplugInitError.html +0 -218
  124. package/doc/classes/ButtplugLogger.html +0 -288
  125. package/doc/classes/ButtplugMessage.html +0 -147
  126. package/doc/classes/ButtplugMessageError.html +0 -218
  127. package/doc/classes/ButtplugMessageSorter.html +0 -128
  128. package/doc/classes/ButtplugNodeWebsocketClientConnector.html +0 -239
  129. package/doc/classes/ButtplugPingError.html +0 -218
  130. package/doc/classes/ButtplugSystemMessage.html +0 -150
  131. package/doc/classes/ButtplugUnknownError.html +0 -218
  132. package/doc/classes/DeviceAdded.html +0 -186
  133. package/doc/classes/DeviceInfo.html +0 -114
  134. package/doc/classes/DeviceList.html +0 -160
  135. package/doc/classes/DeviceRemoved.html +0 -158
  136. package/doc/classes/Error.html +0 -179
  137. package/doc/classes/GenericDeviceMessageAttributes.html +0 -107
  138. package/doc/classes/GenericMessageSubcommand.html +0 -90
  139. package/doc/classes/LinearCmd.html +0 -187
  140. package/doc/classes/LogMessage.html +0 -134
  141. package/doc/classes/MessageAttributes.html +0 -160
  142. package/doc/classes/Ok.html +0 -151
  143. package/doc/classes/Ping.html +0 -151
  144. package/doc/classes/RawDeviceMessageAttributes.html +0 -86
  145. package/doc/classes/RawReadCmd.html +0 -188
  146. package/doc/classes/RawReading.html +0 -179
  147. package/doc/classes/RawSubscribeCmd.html +0 -170
  148. package/doc/classes/RawUnsubscribeCmd.html +0 -170
  149. package/doc/classes/RawWriteCmd.html +0 -188
  150. package/doc/classes/RequestDeviceList.html +0 -151
  151. package/doc/classes/RequestServerInfo.html +0 -169
  152. package/doc/classes/RotateCmd.html +0 -187
  153. package/doc/classes/RotateSubcommand.html +0 -108
  154. package/doc/classes/ScalarCmd.html +0 -170
  155. package/doc/classes/ScalarSubcommand.html +0 -108
  156. package/doc/classes/ScanningFinished.html +0 -146
  157. package/doc/classes/SensorDeviceMessageAttributes.html +0 -107
  158. package/doc/classes/SensorReadCmd.html +0 -179
  159. package/doc/classes/SensorReading.html +0 -188
  160. package/doc/classes/ServerInfo.html +0 -178
  161. package/doc/classes/StartScanning.html +0 -151
  162. package/doc/classes/StopAllDevices.html +0 -151
  163. package/doc/classes/StopDeviceCmd.html +0 -161
  164. package/doc/classes/StopScanning.html +0 -151
  165. package/doc/classes/VectorSubcommand.html +0 -108
  166. package/doc/enums/ActuatorType.html +0 -104
  167. package/doc/enums/ButtplugLogLevel.html +0 -97
  168. package/doc/enums/ErrorClass.html +0 -90
  169. package/doc/enums/SensorType.html +0 -90
  170. package/doc/functions/FromJSON.html +0 -113
  171. package/doc/index.html +0 -184
  172. package/doc/interfaces/IButtplugClientConnector.html +0 -137
  173. package/doc/modules.html +0 -176
  174. package/doc/variables/DEFAULT_MESSAGE_ID.html +0 -104
  175. package/doc/variables/MAX_ID.html +0 -104
  176. package/doc/variables/MESSAGE_SPEC_VERSION.html +0 -104
  177. package/doc/variables/SYSTEM_MESSAGE_ID.html +0 -104
  178. package/src/client/Client.ts +0 -276
  179. package/src/core/MessageUtils.ts +0 -48
@@ -1,401 +1,178 @@
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
-
9
- 'use strict';
10
- import * as Messages from '../core/Messages';
11
- import {
12
- ButtplugDeviceError,
13
- ButtplugError,
14
- ButtplugMessageError,
15
- } from '../core/Exceptions';
16
- import { EventEmitter } from 'eventemitter3';
17
- import { getMessageClassFromMessage } from '../core/MessageUtils';
18
-
19
- /**
20
- * Represents an abstract device, capable of taking certain kinds of messages.
21
- */
22
- export class ButtplugClientDevice extends EventEmitter {
23
- /**
24
- * Return the name of the device.
25
- */
26
- public get name(): string {
27
- return this._deviceInfo.DeviceName;
28
- }
29
-
30
- /**
31
- * Return the user set name of the device.
32
- */
33
- public get displayName(): string | undefined {
34
- return this._deviceInfo.DeviceDisplayName;
35
- }
36
-
37
- /**
38
- * Return the index of the device.
39
- */
40
- public get index(): number {
41
- return this._deviceInfo.DeviceIndex;
42
- }
43
-
44
- /**
45
- * Return the index of the device.
46
- */
47
- public get messageTimingGap(): number | undefined {
48
- return this._deviceInfo.DeviceMessageTimingGap;
49
- }
50
-
51
- /**
52
- * Return a list of message types the device accepts.
53
- */
54
- public get messageAttributes(): Messages.MessageAttributes {
55
- return this._deviceInfo.DeviceMessages;
56
- }
57
-
58
- public static fromMsg(
59
- msg: Messages.DeviceInfo,
60
- sendClosure: (
61
- device: ButtplugClientDevice,
62
- msg: Messages.ButtplugDeviceMessage
63
- ) => Promise<Messages.ButtplugMessage>
64
- ): ButtplugClientDevice {
65
- return new ButtplugClientDevice(msg, sendClosure);
66
- }
67
-
68
- // Map of messages and their attributes (feature count, etc...)
69
- private allowedMsgs: Map<string, Messages.MessageAttributes> = new Map<
70
- string,
71
- Messages.MessageAttributes
72
- >();
73
-
74
- /**
75
- * @param _index Index of the device, as created by the device manager.
76
- * @param _name Name of the device.
77
- * @param allowedMsgs Buttplug messages the device can receive.
78
- */
79
- constructor(
80
- private _deviceInfo: Messages.DeviceInfo,
81
- private _sendClosure: (
82
- device: ButtplugClientDevice,
83
- msg: Messages.ButtplugDeviceMessage
84
- ) => Promise<Messages.ButtplugMessage>
85
- ) {
86
- super();
87
- _deviceInfo.DeviceMessages.update();
88
- }
89
-
90
- public async send(
91
- msg: Messages.ButtplugDeviceMessage
92
- ): Promise<Messages.ButtplugMessage> {
93
- // Assume we're getting the closure from ButtplugClient, which does all of
94
- // the index/existence/connection/message checks for us.
95
- return await this._sendClosure(this, msg);
96
- }
97
-
98
- public async sendExpectOk(
99
- msg: Messages.ButtplugDeviceMessage
100
- ): Promise<void> {
101
- const response = await this.send(msg);
102
- switch (getMessageClassFromMessage(response)) {
103
- case Messages.Ok:
104
- return;
105
- case Messages.Error:
106
- throw ButtplugError.FromError(response as Messages.Error);
107
- default:
108
- throw new ButtplugMessageError(
109
- `Message type ${response.constructor} not handled by SendMsgExpectOk`
110
- );
111
- }
112
- }
113
-
114
- public async scalar(
115
- scalar: Messages.ScalarSubcommand | Messages.ScalarSubcommand[]
116
- ): Promise<void> {
117
- if (Array.isArray(scalar)) {
118
- await this.sendExpectOk(new Messages.ScalarCmd(scalar, this.index));
119
- } else {
120
- await this.sendExpectOk(new Messages.ScalarCmd([scalar], this.index));
121
- }
122
- }
123
-
124
- private async scalarCommandBuilder(
125
- speed: number | number[],
126
- actuator: Messages.ActuatorType
127
- ) {
128
- const scalarAttrs = this.messageAttributes.ScalarCmd?.filter(
129
- (x) => x.ActuatorType === actuator
130
- );
131
- if (!scalarAttrs || scalarAttrs.length === 0) {
132
- throw new ButtplugDeviceError(
133
- `Device ${this.name} has no ${actuator} capabilities`
134
- );
135
- }
136
- const cmds: Messages.ScalarSubcommand[] = [];
137
- if (typeof speed === 'number') {
138
- scalarAttrs.forEach((x) =>
139
- cmds.push(new Messages.ScalarSubcommand(x.Index, speed, actuator))
140
- );
141
- } else if (Array.isArray(speed)) {
142
- if (speed.length > scalarAttrs.length) {
143
- throw new ButtplugDeviceError(
144
- `${speed.length} commands send to a device with ${scalarAttrs.length} vibrators`
145
- );
146
- }
147
- scalarAttrs.forEach((x, i) => {
148
- cmds.push(new Messages.ScalarSubcommand(x.Index, speed[i], actuator));
149
- });
150
- } else {
151
- throw new ButtplugDeviceError(
152
- `${actuator} can only take numbers or arrays of numbers.`
153
- );
154
- }
155
- await this.scalar(cmds);
156
- }
157
-
158
- public get vibrateAttributes(): Messages.GenericDeviceMessageAttributes[] {
159
- return (
160
- this.messageAttributes.ScalarCmd?.filter(
161
- (x) => x.ActuatorType === Messages.ActuatorType.Vibrate
162
- ) ?? []
163
- );
164
- }
165
-
166
- public async vibrate(speed: number | number[]): Promise<void> {
167
- await this.scalarCommandBuilder(speed, Messages.ActuatorType.Vibrate);
168
- }
169
-
170
- public get oscillateAttributes(): Messages.GenericDeviceMessageAttributes[] {
171
- return (
172
- this.messageAttributes.ScalarCmd?.filter(
173
- (x) => x.ActuatorType === Messages.ActuatorType.Oscillate
174
- ) ?? []
175
- );
176
- }
177
-
178
- public async oscillate(speed: number | number[]): Promise<void> {
179
- await this.scalarCommandBuilder(speed, Messages.ActuatorType.Oscillate);
180
- }
181
-
182
- public get rotateAttributes(): Messages.GenericDeviceMessageAttributes[] {
183
- return this.messageAttributes.RotateCmd ?? [];
184
- }
185
-
186
- public async rotate(
187
- values: number | [number, boolean][],
188
- clockwise?: boolean
189
- ): Promise<void> {
190
- const rotateAttrs = this.messageAttributes.RotateCmd;
191
- if (!rotateAttrs || rotateAttrs.length === 0) {
192
- throw new ButtplugDeviceError(
193
- `Device ${this.name} has no Rotate capabilities`
194
- );
195
- }
196
- let msg: Messages.RotateCmd;
197
- if (typeof values === 'number') {
198
- msg = Messages.RotateCmd.Create(
199
- this.index,
200
- new Array(rotateAttrs.length).fill([values, clockwise])
201
- );
202
- } else if (Array.isArray(values)) {
203
- msg = Messages.RotateCmd.Create(this.index, values);
204
- } else {
205
- throw new ButtplugDeviceError(
206
- 'SendRotateCmd can only take a number and boolean, or an array of number/boolean tuples'
207
- );
208
- }
209
- await this.sendExpectOk(msg);
210
- }
211
-
212
- public get linearAttributes(): Messages.GenericDeviceMessageAttributes[] {
213
- return this.messageAttributes.LinearCmd ?? [];
214
- }
215
-
216
- public async linear(
217
- values: number | [number, number][],
218
- duration?: number
219
- ): Promise<void> {
220
- const linearAttrs = this.messageAttributes.LinearCmd;
221
- if (!linearAttrs || linearAttrs.length === 0) {
222
- throw new ButtplugDeviceError(
223
- `Device ${this.name} has no Linear capabilities`
224
- );
225
- }
226
- let msg: Messages.LinearCmd;
227
- if (typeof values === 'number') {
228
- msg = Messages.LinearCmd.Create(
229
- this.index,
230
- new Array(linearAttrs.length).fill([values, duration])
231
- );
232
- } else if (Array.isArray(values)) {
233
- msg = Messages.LinearCmd.Create(this.index, values);
234
- } else {
235
- throw new ButtplugDeviceError(
236
- 'SendLinearCmd can only take a number and number, or an array of number/number tuples'
237
- );
238
- }
239
- await this.sendExpectOk(msg);
240
- }
241
-
242
- public async sensorRead(
243
- sensorIndex: number,
244
- sensorType: Messages.SensorType
245
- ): Promise<number[]> {
246
- const response = await this.send(
247
- new Messages.SensorReadCmd(this.index, sensorIndex, sensorType)
248
- );
249
- switch (getMessageClassFromMessage(response)) {
250
- case Messages.SensorReading:
251
- return (response as Messages.SensorReading).Data;
252
- case Messages.Error:
253
- throw ButtplugError.FromError(response as Messages.Error);
254
- default:
255
- throw new ButtplugMessageError(
256
- `Message type ${response.constructor} not handled by sensorRead`
257
- );
258
- }
259
- }
260
-
261
- public get hasBattery(): boolean {
262
- const batteryAttrs = this.messageAttributes.SensorReadCmd?.filter(
263
- (x) => x.SensorType === Messages.SensorType.Battery
264
- );
265
- return batteryAttrs !== undefined && batteryAttrs.length > 0;
266
- }
267
-
268
- public async battery(): Promise<number> {
269
- if (!this.hasBattery) {
270
- throw new ButtplugDeviceError(
271
- `Device ${this.name} has no Battery capabilities`
272
- );
273
- }
274
- const batteryAttrs = this.messageAttributes.SensorReadCmd?.filter(
275
- (x) => x.SensorType === Messages.SensorType.Battery
276
- );
277
- // Find the battery sensor, we'll need its index.
278
- const result = await this.sensorRead(
279
- batteryAttrs![0].Index,
280
- Messages.SensorType.Battery
281
- );
282
- return result[0] / 100.0;
283
- }
284
-
285
- public get hasRssi(): boolean {
286
- const rssiAttrs = this.messageAttributes.SensorReadCmd?.filter(
287
- (x) => x.SensorType === Messages.SensorType.RSSI
288
- );
289
- return rssiAttrs !== undefined && rssiAttrs.length === 0;
290
- }
291
-
292
- public async rssi(): Promise<number> {
293
- if (!this.hasRssi) {
294
- throw new ButtplugDeviceError(
295
- `Device ${this.name} has no RSSI capabilities`
296
- );
297
- }
298
- const rssiAttrs = this.messageAttributes.SensorReadCmd?.filter(
299
- (x) => x.SensorType === Messages.SensorType.RSSI
300
- );
301
- // Find the battery sensor, we'll need its index.
302
- const result = await this.sensorRead(
303
- rssiAttrs![0].Index,
304
- Messages.SensorType.RSSI
305
- );
306
- return result[0];
307
- }
308
-
309
- public async rawRead(
310
- endpoint: string,
311
- expectedLength: number,
312
- timeout: number
313
- ): Promise<Uint8Array> {
314
- if (!this.messageAttributes.RawReadCmd) {
315
- throw new ButtplugDeviceError(
316
- `Device ${this.name} has no raw read capabilities`
317
- );
318
- }
319
- if (this.messageAttributes.RawReadCmd.Endpoints.indexOf(endpoint) === -1) {
320
- throw new ButtplugDeviceError(
321
- `Device ${this.name} has no raw readable endpoint ${endpoint}`
322
- );
323
- }
324
- const response = await this.send(
325
- new Messages.RawReadCmd(this.index, endpoint, expectedLength, timeout)
326
- );
327
- switch (getMessageClassFromMessage(response)) {
328
- case Messages.RawReading:
329
- return new Uint8Array((response as Messages.RawReading).Data);
330
- case Messages.Error:
331
- throw ButtplugError.FromError(response as Messages.Error);
332
- default:
333
- throw new ButtplugMessageError(
334
- `Message type ${response.constructor} not handled by rawRead`
335
- );
336
- }
337
- }
338
-
339
- public async rawWrite(
340
- endpoint: string,
341
- data: Uint8Array,
342
- writeWithResponse: boolean
343
- ): Promise<void> {
344
- if (!this.messageAttributes.RawWriteCmd) {
345
- throw new ButtplugDeviceError(
346
- `Device ${this.name} has no raw write capabilities`
347
- );
348
- }
349
- if (this.messageAttributes.RawWriteCmd.Endpoints.indexOf(endpoint) === -1) {
350
- throw new ButtplugDeviceError(
351
- `Device ${this.name} has no raw writable endpoint ${endpoint}`
352
- );
353
- }
354
- await this.sendExpectOk(
355
- new Messages.RawWriteCmd(this.index, endpoint, data, writeWithResponse)
356
- );
357
- }
358
-
359
- public async rawSubscribe(endpoint: string): Promise<void> {
360
- if (!this.messageAttributes.RawSubscribeCmd) {
361
- throw new ButtplugDeviceError(
362
- `Device ${this.name} has no raw subscribe capabilities`
363
- );
364
- }
365
- if (
366
- this.messageAttributes.RawSubscribeCmd.Endpoints.indexOf(endpoint) === -1
367
- ) {
368
- throw new ButtplugDeviceError(
369
- `Device ${this.name} has no raw subscribable endpoint ${endpoint}`
370
- );
371
- }
372
- await this.sendExpectOk(new Messages.RawSubscribeCmd(this.index, endpoint));
373
- }
374
-
375
- public async rawUnsubscribe(endpoint: string): Promise<void> {
376
- // This reuses raw subscribe's info.
377
- if (!this.messageAttributes.RawSubscribeCmd) {
378
- throw new ButtplugDeviceError(
379
- `Device ${this.name} has no raw unsubscribe capabilities`
380
- );
381
- }
382
- if (
383
- this.messageAttributes.RawSubscribeCmd.Endpoints.indexOf(endpoint) === -1
384
- ) {
385
- throw new ButtplugDeviceError(
386
- `Device ${this.name} has no raw unsubscribable endpoint ${endpoint}`
387
- );
388
- }
389
- await this.sendExpectOk(
390
- new Messages.RawUnsubscribeCmd(this.index, endpoint)
391
- );
392
- }
393
-
394
- public async stop(): Promise<void> {
395
- await this.sendExpectOk(new Messages.StopDeviceCmd(this.index));
396
- }
397
-
398
- public emitDisconnected() {
399
- this.emit('deviceremoved');
400
- }
401
- }
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
+
9
+ 'use strict';
10
+ import * as Messages from '../core/Messages';
11
+ import {
12
+ ButtplugDeviceError,
13
+ ButtplugError,
14
+ ButtplugMessageError,
15
+ } from '../core/Exceptions';
16
+ import { EventEmitter } from 'eventemitter3';
17
+ import { ButtplugClientDeviceFeature } from './ButtplugClientDeviceFeature';
18
+ import { DeviceOutputCommand } from './ButtplugClientDeviceCommand';
19
+
20
+ /**
21
+ * Represents an abstract device, capable of taking certain kinds of messages.
22
+ */
23
+ export class ButtplugClientDevice extends EventEmitter {
24
+
25
+ private _features: Map<number, ButtplugClientDeviceFeature>;
26
+
27
+ /**
28
+ * Return the name of the device.
29
+ */
30
+ public get name(): string {
31
+ return this._deviceInfo.DeviceName;
32
+ }
33
+
34
+ /**
35
+ * Return the user set name of the device.
36
+ */
37
+ public get displayName(): string | undefined {
38
+ return this._deviceInfo.DeviceDisplayName;
39
+ }
40
+
41
+ /**
42
+ * Return the index of the device.
43
+ */
44
+ public get index(): number {
45
+ return this._deviceInfo.DeviceIndex;
46
+ }
47
+
48
+ /**
49
+ * Return the index of the device.
50
+ */
51
+ public get messageTimingGap(): number | undefined {
52
+ return this._deviceInfo.DeviceMessageTimingGap;
53
+ }
54
+
55
+ public get features(): Map<number, ButtplugClientDeviceFeature> {
56
+ return this._features;
57
+ }
58
+
59
+ // /**
60
+ // * Return a list of message types the device accepts.
61
+ // */
62
+ // public get messageAttributes(): Messages.MessageAttributes {
63
+ // return this._deviceInfo.DeviceMessages;
64
+ // }
65
+ //
66
+ public static fromMsg(
67
+ msg: Messages.DeviceInfo,
68
+ sendClosure: (
69
+ msg: Messages.ButtplugMessage
70
+ ) => Promise<Messages.ButtplugMessage>
71
+ ): ButtplugClientDevice {
72
+ return new ButtplugClientDevice(msg, sendClosure);
73
+ }
74
+ //
75
+ // // Map of messages and their attributes (feature count, etc...)
76
+ // private allowedMsgs: Map<string, Messages.MessageAttributes> = new Map<
77
+ // string,
78
+ // Messages.MessageAttributes
79
+ // >();
80
+ //
81
+ /**
82
+ * @param _index Index of the device, as created by the device manager.
83
+ * @param _name Name of the device.
84
+ * @param allowedMsgs Buttplug messages the device can receive.
85
+ */
86
+ private constructor(
87
+ private _deviceInfo: Messages.DeviceInfo,
88
+ private _sendClosure: (
89
+ msg: Messages.ButtplugMessage
90
+ ) => Promise<Messages.ButtplugMessage>
91
+ ) {
92
+ super();
93
+ this._features = new Map(Object.entries(_deviceInfo.DeviceFeatures).map(([index, v]) => [parseInt(index), new ButtplugClientDeviceFeature(_deviceInfo.DeviceIndex, _deviceInfo.DeviceName, v, _sendClosure)]));
94
+ }
95
+
96
+ public async send(
97
+ msg: Messages.ButtplugMessage
98
+ ): Promise<Messages.ButtplugMessage> {
99
+ // Assume we're getting the closure from ButtplugClient, which does all of
100
+ // the index/existence/connection/message checks for us.
101
+ return await this._sendClosure(msg);
102
+ }
103
+
104
+ protected sendMsgExpectOk = async (
105
+ msg: Messages.ButtplugMessage
106
+ ): Promise<void> => {
107
+ const response = await this.send(msg);
108
+ if (response.Ok !== undefined) {
109
+ return;
110
+ } else if (response.Error !== undefined) {
111
+ throw ButtplugError.FromError(response as Messages.Error);
112
+ } else {
113
+ /*
114
+ throw ButtplugError.LogAndError(
115
+ ButtplugMessageError,
116
+ this._logger,
117
+ `Message ${response} not handled by SendMsgExpectOk`
118
+ );
119
+ */
120
+ }
121
+ };
122
+
123
+ protected isOutputValid(featureIndex: number, type: Messages.OutputType) {
124
+ if (!this._deviceInfo.DeviceFeatures.hasOwnProperty(featureIndex.toString())) {
125
+ throw new ButtplugDeviceError(`Feature index ${featureIndex} does not exist for device ${this.name}`);
126
+ }
127
+ if (this._deviceInfo.DeviceFeatures[featureIndex.toString()].Outputs !== undefined && !this._deviceInfo.DeviceFeatures[featureIndex.toString()].Outputs.hasOwnProperty(type)) {
128
+ throw new ButtplugDeviceError(`Feature index ${featureIndex} does not support type ${type} for device ${this.name}`);
129
+ }
130
+ }
131
+
132
+ public hasOutput(type: Messages.OutputType): boolean {
133
+ return this._features.values().filter((f) => f.hasOutput(type)).toArray().length > 0;
134
+ }
135
+
136
+ public hasInput(type: Messages.InputType): boolean {
137
+ return this._features.values().filter((f) => f.hasInput(type)).toArray().length > 0;
138
+ }
139
+
140
+ public async runOutput(cmd: DeviceOutputCommand): Promise<void> {
141
+ let p: Promise<void>[] = [];
142
+ for (let f of this._features.values()) {
143
+ if (f.hasOutput(cmd.outputType)) {
144
+ p.push(f.runOutput(cmd));
145
+ }
146
+ }
147
+ if (p.length == 0) {
148
+ return Promise.reject(`No features with output type ${cmd.outputType}`);
149
+ }
150
+ await Promise.all(p);
151
+ }
152
+
153
+ public async stop(): Promise<void> {
154
+ await this.sendMsgExpectOk({StopCmd: { Id: 1, DeviceIndex: this.index, FeatureIndex: undefined, Inputs: true, Outputs: true}});
155
+ }
156
+
157
+ public async battery(): Promise<number> {
158
+ let p: Promise<void>[] = [];
159
+ for (let f of this._features.values()) {
160
+ if (f.hasInput(Messages.InputType.Battery)) {
161
+ // Right now, we only have one battery per device, so assume the first one we find is it.
162
+ let response = await f.runInput(Messages.InputType.Battery, Messages.InputCommandType.Read);
163
+ if (response === undefined) {
164
+ throw new ButtplugMessageError("Got incorrect message back.");
165
+ }
166
+ if (response.Reading[Messages.InputType.Battery] === undefined) {
167
+ throw new ButtplugMessageError("Got reading with no Battery info.");
168
+ }
169
+ return response.Reading[Messages.InputType.Battery].Value;
170
+ }
171
+ }
172
+ throw new ButtplugDeviceError(`No battery present on this device.`);
173
+ }
174
+
175
+ public emitDisconnected() {
176
+ this.emit('deviceremoved');
177
+ }
178
+ }