buttplug 1.0.17 → 3.0.0-alpha.1

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 (119) hide show
  1. package/.eslintrc +13 -0
  2. package/.github/FUNDING.yml +5 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +17 -0
  4. package/.jscsrc +3 -0
  5. package/.jshintrc +6 -0
  6. package/.yarnrc.yml +1 -0
  7. package/CHANGELOG.md +377 -24
  8. package/CODE_OF_CONDUCT.md +166 -0
  9. package/CONTRIBUTING.md +169 -0
  10. package/LICENSE +27 -0
  11. package/README.md +23 -126
  12. package/azure-pipelines.yml +19 -0
  13. package/dist/main/src/client/ButtplugBrowserWebsocketClientConnector.d.ts +19 -0
  14. package/dist/main/src/client/ButtplugBrowserWebsocketClientConnector.js +58 -0
  15. package/dist/main/src/client/ButtplugBrowserWebsocketClientConnector.js.map +1 -0
  16. package/dist/main/src/client/ButtplugClientConnectorException.d.ts +11 -0
  17. package/dist/main/src/client/ButtplugClientConnectorException.js +42 -0
  18. package/dist/main/src/client/ButtplugClientConnectorException.js.map +1 -0
  19. package/dist/main/src/client/ButtplugClientDevice.d.ts +62 -0
  20. package/dist/main/src/client/ButtplugClientDevice.js +299 -0
  21. package/dist/main/src/client/ButtplugClientDevice.js.map +1 -0
  22. package/dist/main/src/client/Client.d.ts +42 -0
  23. package/dist/main/src/client/Client.js +237 -0
  24. package/dist/main/src/client/Client.js.map +1 -0
  25. package/dist/main/src/client/IButtplugClientConnector.d.ts +17 -0
  26. package/dist/main/src/client/IButtplugClientConnector.js +10 -0
  27. package/dist/main/src/client/IButtplugClientConnector.js.map +1 -0
  28. package/dist/main/src/core/Exceptions.d.ts +36 -0
  29. package/dist/main/src/core/Exceptions.js +107 -0
  30. package/dist/main/src/core/Exceptions.js.map +1 -0
  31. package/dist/main/src/core/Logging.d.ts +113 -0
  32. package/dist/main/src/core/Logging.js +170 -0
  33. package/dist/main/src/core/Logging.js.map +1 -0
  34. package/dist/main/src/core/MessageUtils.d.ts +9 -0
  35. package/dist/main/src/core/MessageUtils.js +52 -0
  36. package/dist/main/src/core/MessageUtils.js.map +1 -0
  37. package/dist/main/src/core/Messages.d.ts +260 -0
  38. package/dist/main/src/core/Messages.js +412 -0
  39. package/dist/main/src/core/Messages.js.map +1 -0
  40. package/dist/main/src/index.d.ts +17 -0
  41. package/dist/main/src/index.js +34 -0
  42. package/dist/main/src/index.js.map +1 -0
  43. package/dist/main/src/utils/ButtplugBrowserWebsocketConnector.d.ts +22 -0
  44. package/dist/main/src/utils/ButtplugBrowserWebsocketConnector.js +92 -0
  45. package/dist/main/src/utils/ButtplugBrowserWebsocketConnector.js.map +1 -0
  46. package/dist/main/src/utils/ButtplugMessageSorter.d.ts +16 -0
  47. package/dist/main/src/utils/ButtplugMessageSorter.js +79 -0
  48. package/dist/main/src/utils/ButtplugMessageSorter.js.map +1 -0
  49. package/dist/main/src/utils/Utils.d.ts +1 -0
  50. package/dist/main/src/utils/Utils.js +8 -0
  51. package/dist/main/src/utils/Utils.js.map +1 -0
  52. package/dist/web/buttplug.js +6087 -16541
  53. package/dist/web/buttplug.min.js +2 -45
  54. package/dist/web/buttplug.min.js.LICENSE.txt +31 -0
  55. package/dist/web/buttplug.min.js.map +1 -1
  56. package/dist/web/index.html +10 -0
  57. package/jest-puppeteer.config.js +5 -0
  58. package/package.json +93 -44
  59. package/rollup.config.js +55 -0
  60. package/src/client/ButtplugBrowserWebsocketClientConnector.ts +56 -0
  61. package/src/client/ButtplugClientConnectorException.ts +16 -0
  62. package/src/client/ButtplugClientDevice.ts +255 -0
  63. package/src/client/Client.ts +233 -0
  64. package/src/client/IButtplugClientConnector.ts +18 -0
  65. package/src/core/Exceptions.ts +98 -0
  66. package/src/core/Logging.ts +194 -0
  67. package/src/core/MessageUtils.ts +26 -0
  68. package/src/core/Messages.ts +421 -0
  69. package/src/core/index.d.ts +4 -0
  70. package/src/index.ts +18 -0
  71. package/src/utils/ButtplugBrowserWebsocketConnector.ts +90 -0
  72. package/src/utils/ButtplugMessageSorter.ts +54 -0
  73. package/src/utils/Utils.ts +3 -0
  74. package/tsconfig.json +6 -5
  75. package/tslint.json +27 -0
  76. package/typedoc.js +9 -0
  77. package/util/convert_device_config.js +6 -0
  78. package/.netlify/state.json +0 -3
  79. package/LICENSE.md +0 -29
  80. package/dist/module/buttplug-rs-ffi/buttplug_rs_ffi.d.ts +0 -41
  81. package/dist/module/buttplug-rs-ffi/buttplug_rs_ffi.js +0 -2
  82. package/dist/module/buttplug-rs-ffi/buttplug_rs_ffi_bg.js +0 -6
  83. package/dist/module/buttplug-rs-ffi/buttplug_rs_ffi_bg.wasm +0 -0
  84. package/dist/module/buttplug-rs-ffi/buttplug_rs_ffi_bg.wasm.d.ts +0 -18
  85. package/dist/module/buttplug-rs-ffi/buttplug_rs_ffi_bg_node.js +0 -799
  86. package/dist/module/buttplug-rs-ffi/buttplug_rs_ffi_bg_web.js +0 -788
  87. package/dist/module/buttplug_ffi.d.ts +0 -3898
  88. package/dist/module/buttplug_ffi.js +0 -10541
  89. package/dist/module/client.d.ts +0 -30
  90. package/dist/module/client.js +0 -126
  91. package/dist/module/client.js.map +0 -1
  92. package/dist/module/connectors.d.ts +0 -13
  93. package/dist/module/connectors.js +0 -26
  94. package/dist/module/connectors.js.map +0 -1
  95. package/dist/module/device.d.ts +0 -85
  96. package/dist/module/device.js +0 -217
  97. package/dist/module/device.js.map +0 -1
  98. package/dist/module/errors.d.ts +0 -25
  99. package/dist/module/errors.js +0 -60
  100. package/dist/module/errors.js.map +0 -1
  101. package/dist/module/ffi.d.ts +0 -25
  102. package/dist/module/ffi.js +0 -243
  103. package/dist/module/ffi.js.map +0 -1
  104. package/dist/module/index.d.ts +0 -15
  105. package/dist/module/index.js +0 -16
  106. package/dist/module/index.js.map +0 -1
  107. package/dist/module/sorter.d.ts +0 -15
  108. package/dist/module/sorter.js +0 -43
  109. package/dist/module/sorter.js.map +0 -1
  110. package/dist/module/web_index.d.ts +0 -8
  111. package/dist/module/web_index.js +0 -12
  112. package/dist/module/web_index.js.map +0 -1
  113. package/dist/web/0.buttplug.js +0 -1389
  114. package/dist/web/1.buttplug.min.js +0 -2
  115. package/dist/web/1.buttplug.min.js.map +0 -1
  116. package/dist/web/b0219b34bc18e1ad0240.module.wasm +0 -0
  117. package/dist/web/e5566a8b6a3fda978549.module.wasm +0 -0
  118. package/index.html +0 -25
  119. package/webpack.base.cjs +0 -107
@@ -0,0 +1,10 @@
1
+ <html>
2
+ <head>
3
+ <script type="text/javascript" src="buttplug.min.js" defer></script>
4
+ </head>
5
+ <body>
6
+ <script>
7
+ </script>
8
+ <h1 class="title">I'm a test page!</h1>
9
+ </body>
10
+ </html>
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ launch: {
3
+ args: ['--no-sandbox', '--disable-setuid-sandbox'],
4
+ },
5
+ };
package/package.json CHANGED
@@ -1,60 +1,109 @@
1
1
  {
2
2
  "name": "buttplug",
3
- "collaborators": [
4
- "Nonpolynomial, LLC <kyle@nonpolynomial.com>"
5
- ],
6
- "description": "Buttplug WASM library for web applications (Node/Web). Does not work for native node at the moment.",
7
- "version": "1.0.17",
8
- "license": "BSD-3-Clause",
9
- "homepage": "https://buttplug.io",
3
+ "version": "3.0.0-alpha.1",
4
+ "description": "Buttplug Client Implementation for Typescript/Javascript",
5
+ "homepage": "https://github.com/buttplugio/buttplug-js/",
10
6
  "repository": {
11
7
  "type": "git",
12
- "url": "https://github.com/buttplugio/buttplug-rs-ffi"
8
+ "url": "git+https://github.com/buttplugio/buttplug-js.git"
13
9
  },
10
+ "author": "Nonpolynomial Labs, LLC",
11
+ "keywords": [
12
+ "teledildonics",
13
+ "hardware"
14
+ ],
15
+ "license": "BSD-3-Clause",
14
16
  "bugs": {
15
- "url": "https://github.com/buttplugio/buttplug-rs-ffi/issues"
17
+ "url": "https://github.com/buttplugio/buttplug-js/issues"
16
18
  },
19
+ "main": "./dist/main/src/index.js",
20
+ "types": "./dist/main/src/index.d.ts",
17
21
  "scripts": {
18
- "dev": "webpack-dev-server --hot --config webpack.base.cjs --env development",
19
- "build:publish": "rimraf dist && yarn build:rust && yarn build:webpack && yarn build:webpack:production",
20
- "build:rust": "rimraf src/buttplug-rs-ffi && cd ../ffi && cross-env RUSTFLAGS=\"--cfg=web_sys_unstable_apis\" wasm-pack build -d../js/src/buttplug-rs-ffi --release -- --features wasm --no-default-features && cd ../js && node scripts/modularize.cjs wasm-pack && rimraf src/buttplug-rs-ffi/.gitignore src/buttplug-rs-ffi/package.json src/buttplug-rs-ffi/README.md",
21
- "build:main": "tsc -p tsconfig.json && copyfiles -u 1 \"src/**/*.js\" dist/module && copyfiles -u 1 \"src/**/*.d.ts\" dist/module && copyfiles -u 1 \"src/**/*.wasm\" dist/module",
22
- "build:proto": "pbjs -t static-module -w es6 -o src/buttplug_ffi.js ../protobuf_schemas/buttplug_rs_ffi.proto && node scripts/modularize.cjs pbjs && pbjs -t static-module ../protobuf_schemas/buttplug_rs_ffi.proto | pbts -o src/buttplug_ffi.d.ts -",
23
- "build:webpack": "yarn build:main && webpack --progress --config webpack.base.cjs --env development",
24
- "build:webpack:production": "webpack --progress --config webpack.base.cjs --env production"
22
+ "build": "trash dist dist-bundle && yarn build:all",
23
+ "build:all": "yarn build:main && yarn build:web && yarn build:web:release",
24
+ "build:main": "tsc -p tsconfig.json",
25
+ "build:doc": "typedoc --options typedoc.js --out doc .",
26
+ "build:rollup": "rollup -c",
27
+ "tslint": "tslint --project tsconfig.json --outputAbsolutePaths -c ../../tslint.json \"src/**/*.ts\" \"tests/**/*.ts\"",
28
+ "tslint:fix": "tslint --project tsconfig.json --outputAbsolutePaths -c ../../tslint.json --fix semicolon,comma,no-var-keyword,ordered-imports \"src/**/*.ts\" \"tests/**/*.ts\"",
29
+ "build:web": "webpack --config=build/webpack.base.js",
30
+ "build:web:release": "webpack --progress --hide-modules --config=build/webpack.production.js",
31
+ "build:analyze": "webpack --hide-modules --config=build/webpack.analyzer.js",
32
+ "pretest": "yarn build:main",
33
+ "test": "jest tests/*",
34
+ "web-test": "jest web-tests/test-web-library.ts",
35
+ "web-test-ci": "jest --runInBand web-tests/test-web-library.ts"
25
36
  },
26
- "sideEffects": "false",
27
- "type": "module",
28
- "main": "./dist/module/index.js",
29
- "exports": {
30
- "node": "./dist/module/index.js",
31
- "browser": "./dist/web/buttplug.min.js"
32
- },
33
- "imports": {
34
- "#buttplug_rs_ffi_bg": {
35
- "node": "./dist/module/buttplug-rs-ffi/buttplug_rs_ffi_bg_node.js"
36
- }
37
- },
38
- "types": "./dist/module/index.d.ts",
39
37
  "dependencies": {
40
- "protobufjs": "^6.11.2",
41
- "websocket": "^1.0.34"
38
+ "class-transformer": "^0.5.1",
39
+ "reflect-metadata": "^0.1.13"
42
40
  },
43
41
  "devDependencies": {
42
+ "@types/commander": "^2.12.2",
43
+ "@types/expect-puppeteer": "^5.0.2",
44
+ "@types/jest": "^29.2.4",
45
+ "@types/jest-environment-puppeteer": "^5.0.3",
46
+ "@types/node": "^18.11.18",
47
+ "@types/uuid-parse": "^1.0.0",
48
+ "@types/ws": "^8.5.3",
44
49
  "copyfiles": "^2.4.1",
45
50
  "cross-env": "^7.0.3",
46
- "fork-ts-checker-webpack-plugin": "^6.5.0",
47
- "html-loader": "^3.0.1",
48
- "rimraf": "^3.0.2",
49
- "ts-loader": "^9",
50
- "ts-node": "^10.4.0",
51
- "ts-proto": "^1.93.1",
52
- "typescript": "^4.5.2",
53
- "uglify-js": "^3.14.4",
54
- "webpack": "^4",
55
- "webpack-cli": "^3",
56
- "webpack-dev-server": "^3.11.2",
57
- "webpack-merge": "^5.8.0",
58
- "yarn": "^1.22.17"
51
+ "fork-ts-checker-webpack-plugin": "^7.2.14",
52
+ "jest": "^29.3.1",
53
+ "jest-puppeteer": "^6.2.0",
54
+ "mock-socket": "^9.1.5",
55
+ "pkg": "^5.8.0",
56
+ "puppeteer": "^19.4.1",
57
+ "rollup": "^3.8.1",
58
+ "rollup-plugin-auto-external": "^2.0.0",
59
+ "rollup-plugin-commonjs": "^10.1.0",
60
+ "rollup-plugin-html": "^0.2.1",
61
+ "rollup-plugin-json": "^4.0.0",
62
+ "rollup-plugin-node-builtins": "^2.1.2",
63
+ "rollup-plugin-node-globals": "^1.4.0",
64
+ "rollup-plugin-node-resolve": "^5.2.0",
65
+ "rollup-plugin-postcss": "^4.0.2",
66
+ "rollup-plugin-postcss-modules": "^2.1.0",
67
+ "rollup-plugin-terser": "^5.3.0",
68
+ "rollup-plugin-typescript2": "^0.34.1",
69
+ "selfsigned": "^2.1.1",
70
+ "style-loader": "^3.3.1",
71
+ "terser": "^4.x",
72
+ "terser-webpack-plugin": "^4.x",
73
+ "tmp": "^0.2.1",
74
+ "trash": "^8.1.0",
75
+ "trash-cli": "^5.0.0",
76
+ "ts-jest": "^29.0.3",
77
+ "ts-loader": "^8.x",
78
+ "ts-node": "^10.9.1",
79
+ "tslib": "^2.4.1",
80
+ "tslint": "^6.1.0",
81
+ "typedoc": "^0.23.23",
82
+ "typescript": "^4.9.4",
83
+ "url-loader": "^4.1.1",
84
+ "webpack": "^4.42.1",
85
+ "webpack-bundle-analyzer": "^3.6.1",
86
+ "webpack-cli": "^3.3.11",
87
+ "webpack-merge": "^4.2.2",
88
+ "yarn": "^1.22.19"
89
+ },
90
+ "jest": {
91
+ "moduleFileExtensions": [
92
+ "ts",
93
+ "js",
94
+ "json"
95
+ ],
96
+ "transform": {
97
+ "^.+\\.ts$": "ts-jest"
98
+ },
99
+ "testMatch": [
100
+ "<rootDir>/tests/**/test-*.ts",
101
+ "<rootDir>/web-tests/**/test-*.ts"
102
+ ],
103
+ "coverageDirectory": "./coverage/",
104
+ "coverageReporters": [
105
+ "json"
106
+ ],
107
+ "collectCoverage": true
59
108
  }
60
109
  }
@@ -0,0 +1,55 @@
1
+ import typescript from 'rollup-plugin-typescript2';
2
+ import nodeResolve from 'rollup-plugin-node-resolve';
3
+ import json from 'rollup-plugin-json';
4
+ import { terser } from 'rollup-plugin-terser';
5
+ import commonjs from 'rollup-plugin-commonjs';
6
+ import nodeBuiltins from 'rollup-plugin-node-builtins';
7
+ import nodeGlobals from 'rollup-plugin-node-globals';
8
+
9
+ // Generate our base config object out of a function return, so we don't have to
10
+ // worry about doing copies/clones.
11
+ function configGen() {
12
+ return {
13
+ input: 'src/index.ts',
14
+ output: {
15
+ file: 'dist/web/buttplug.js',
16
+ format: 'iife',
17
+ name: "Buttplug"
18
+ },
19
+ plugins: [
20
+ nodeResolve({
21
+ jsnext: true,
22
+ main: true,
23
+ extensions: [ '.ts', '.js', '.json' ],
24
+ browser: true
25
+ }),
26
+ typescript({ tsconfigOverride: { compilerOptions: { module: "ES2015" } } }),
27
+ json(),
28
+ nodeBuiltins(),
29
+ nodeGlobals(),
30
+ commonjs({
31
+ // non-CommonJS modules will be ignored, but you can also
32
+ // specifically include/exclude files
33
+ include: ['../../node_modules/**', './node_modules/**'], // Default: undefined
34
+ }),
35
+ ]
36
+ };
37
+ }
38
+
39
+ let config = configGen();
40
+
41
+ let configProduction = configGen();
42
+ configProduction.output.file = 'dist/web/buttplug.min.js';
43
+ configProduction.plugins.push(terser({
44
+ sourcemap: true,
45
+ mangle: {
46
+ keep_classnames: true,
47
+ keep_fnames: true
48
+ },
49
+ compress: {
50
+ keep_fnames: true,
51
+ keep_classnames: true,
52
+ }
53
+ }));
54
+
55
+ module.exports = [config, configProduction];
@@ -0,0 +1,56 @@
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
+
11
+ import { IButtplugClientConnector } from "./IButtplugClientConnector";
12
+ import { ButtplugMessage } from "../core/Messages";
13
+ import { FromJSON } from "../core/MessageUtils";
14
+ import { ButtplugMessageSorter } from "../utils/ButtplugMessageSorter";
15
+ import { ButtplugBrowserWebsocketConnector } from "../utils/ButtplugBrowserWebsocketConnector";
16
+
17
+ export class ButtplugBrowserWebsocketClientConnector extends ButtplugBrowserWebsocketConnector implements IButtplugClientConnector {
18
+
19
+ private _sorter: ButtplugMessageSorter = new ButtplugMessageSorter(true);
20
+ protected _ws: WebSocket | undefined;
21
+
22
+ public constructor(_url: string) {
23
+ super(_url);
24
+ }
25
+
26
+ public get Connected(): boolean {
27
+ return this._ws !== undefined;
28
+ }
29
+
30
+ public Send = async (msg: ButtplugMessage): Promise<ButtplugMessage> => {
31
+ if (!this.Connected) {
32
+ throw new Error("ButtplugClient not connected");
33
+ }
34
+ const p = this._sorter.PrepareOutgoingMessage(msg);
35
+ this.SendMessage(msg);
36
+ return await p;
37
+ }
38
+
39
+ protected ParseIncomingMessage = (event: MessageEvent) => {
40
+ if (typeof (event.data) === "string") {
41
+ const msgs = FromJSON(event.data);
42
+ const emitMsgs = this._sorter.ParseIncomingMessages(msgs);
43
+ this.emit("message", emitMsgs);
44
+ } else if (event.data instanceof Blob) {
45
+ const reader = new FileReader();
46
+ reader.addEventListener("load", (ev) => { this.OnReaderLoad(ev); });
47
+ reader.readAsText(event.data);
48
+ }
49
+ }
50
+
51
+ protected OnReaderLoad(event: Event) {
52
+ const msgs = FromJSON((event.target as FileReader).result);
53
+ const emitMsgs = this._sorter.ParseIncomingMessages(msgs);
54
+ this.emit("message", emitMsgs);
55
+ }
56
+ }
@@ -0,0 +1,16 @@
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
+ import { ButtplugException } from "../core/Exceptions";
10
+ import * as Messages from "../core/Messages";
11
+
12
+ export class ButtplugClientConnectorException extends ButtplugException {
13
+ public constructor(message: string) {
14
+ super(message, Messages.ErrorClass.ERROR_UNKNOWN);
15
+ }
16
+ }
@@ -0,0 +1,255 @@
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 { ButtplugDeviceException, ButtplugException, ButtplugMessageException } from "../core/Exceptions";
12
+ import { EventEmitter } from "events";
13
+
14
+ /**
15
+ * Represents an abstract device, capable of taking certain kinds of messages.
16
+ */
17
+ export class ButtplugClientDevice extends EventEmitter {
18
+
19
+ /**
20
+ * Return the name of the device.
21
+ */
22
+ public get Name(): string {
23
+ return this._deviceInfo.DeviceName;
24
+ }
25
+
26
+ /**
27
+ * Return the user set name of the device.
28
+ */
29
+ public get DisplayName(): string | undefined {
30
+ return this._deviceInfo.DeviceDisplayName;
31
+ }
32
+
33
+ /**
34
+ * Return the index of the device.
35
+ */
36
+ public get Index(): number {
37
+ return this._deviceInfo.DeviceIndex;
38
+ }
39
+
40
+ /**
41
+ * Return the index of the device.
42
+ */
43
+ public get MessageTimingGap(): number | undefined {
44
+ return this._deviceInfo.DeviceMessageTimingGap;
45
+ }
46
+
47
+ /**
48
+ * Return a list of message types the device accepts.
49
+ */
50
+ public get AllowedMessages(): Messages.MessageAttributes {
51
+ return this._deviceInfo.DeviceMessages;
52
+ }
53
+
54
+ public static fromMsg(msg: Messages.DeviceInfo,
55
+ sendClosure: (device: ButtplugClientDevice,
56
+ msg: Messages.ButtplugDeviceMessage) => Promise<Messages.ButtplugMessage>): ButtplugClientDevice {
57
+ return new ButtplugClientDevice(msg,
58
+ sendClosure);
59
+ }
60
+
61
+ // Map of messages and their attributes (feature count, etc...)
62
+ private allowedMsgs: Map<string, Messages.MessageAttributes> = new Map<string, Messages.MessageAttributes>();
63
+
64
+ /**
65
+ * @param _index Index of the device, as created by the device manager.
66
+ * @param _name Name of the device.
67
+ * @param allowedMsgs Buttplug messages the device can receive.
68
+ */
69
+ constructor(private _deviceInfo: Messages.DeviceInfo,
70
+ private _sendClosure: (device: ButtplugClientDevice,
71
+ msg: Messages.ButtplugDeviceMessage) => Promise<Messages.ButtplugMessage>) {
72
+ super();
73
+ _deviceInfo.DeviceMessages.update();
74
+ }
75
+
76
+ public async send(msg: Messages.ButtplugDeviceMessage): Promise<Messages.ButtplugMessage> {
77
+ // Assume we're getting the closure from ButtplugClient, which does all of
78
+ // the index/existence/connection/message checks for us.
79
+ return await this._sendClosure(this, msg);
80
+ }
81
+
82
+ public async sendExpectOk(msg: Messages.ButtplugDeviceMessage): Promise<void> {
83
+ const response = await this.send(msg);
84
+ switch (response.constructor) {
85
+ case Messages.Ok:
86
+ return;
87
+ case Messages.Error:
88
+ throw ButtplugException.FromError(response as Messages.Error);
89
+ default:
90
+ throw new ButtplugMessageException(`Message type ${response.constructor} not handled by SendMsgExpectOk`);
91
+ }
92
+ }
93
+
94
+ public async scalar(scalar: Messages.ScalarSubcommand | Messages.ScalarSubcommand[]): Promise<void> {
95
+ if (Array.isArray(scalar)) {
96
+ await this.sendExpectOk(new Messages.ScalarCmd(scalar, this.Index));
97
+ } else {
98
+ await this.sendExpectOk(new Messages.ScalarCmd([scalar], this.Index));
99
+ }
100
+ }
101
+
102
+ private async scalarCommandBuilder(speed: number | number[], actuator: Messages.ActuatorType) {
103
+ let scalarAttrs = this.AllowedMessages.ScalarCmd?.filter((x) => x.ActuatorType == actuator);
104
+ if (!scalarAttrs || scalarAttrs.length == 0) {
105
+ throw new ButtplugDeviceException(`Device ${this.Name} has no ${actuator} capabilities`);
106
+ }
107
+ let cmds: Messages.ScalarSubcommand[] = [];
108
+ if (typeof(speed) === "number") {
109
+ scalarAttrs.forEach((x) => cmds.push(new Messages.ScalarSubcommand(x.Index, speed, actuator)));
110
+ } else if (Array.isArray(speed)) {
111
+ if (speed.length > scalarAttrs.length) {
112
+ throw new ButtplugDeviceException(`${speed.length} commands send to a device with ${scalarAttrs.length} vibrators`);
113
+ }
114
+ scalarAttrs.forEach((x, i) => {
115
+ cmds.push(new Messages.ScalarSubcommand(x.Index, speed[i], actuator))
116
+ });
117
+ } else {
118
+ throw new ButtplugDeviceException(`${actuator} can only take numbers or arrays of numbers.`);
119
+ }
120
+ await this.scalar(cmds);
121
+ }
122
+
123
+ public async vibrate(speed: number | number[]): Promise<void> {
124
+ await this.scalarCommandBuilder(speed, Messages.ActuatorType.Vibrate);
125
+ }
126
+
127
+ public async oscillate(speed: number | number[]): Promise<void> {
128
+ await this.scalarCommandBuilder(speed, Messages.ActuatorType.Oscillate);
129
+ }
130
+
131
+ public async rotate(values: number | [number, boolean][], clockwise?: boolean): Promise<void> {
132
+ let rotateAttrs = this.AllowedMessages.RotateCmd;
133
+ if (!rotateAttrs || rotateAttrs.length == 0) {
134
+ throw new ButtplugDeviceException(`Device ${this.Name} has no Rotate capabilities`);
135
+ }
136
+ let msg: Messages.RotateCmd;
137
+ if (typeof(values) === "number") {
138
+ msg = Messages.RotateCmd.Create(this.Index,
139
+ new Array(rotateAttrs.length).fill([values, clockwise]));
140
+ } else if (Array.isArray(values)) {
141
+ msg = Messages.RotateCmd.Create(this.Index, values);
142
+ } else {
143
+ throw new ButtplugDeviceException(
144
+ "SendRotateCmd can only take a number and boolean, or an array of number/boolean tuples");
145
+ }
146
+ await this.sendExpectOk(msg);
147
+ }
148
+
149
+ public async linear(values: number | [number, number][], duration?: number): Promise<void> {
150
+ let linearAttrs = this.AllowedMessages.LinearCmd;
151
+ if (!linearAttrs || linearAttrs.length == 0) {
152
+ throw new ButtplugDeviceException(`Device ${this.Name} has no Linear capabilities`);
153
+ }
154
+ let msg: Messages.LinearCmd;
155
+ if (typeof(values) === "number") {
156
+ msg = Messages.LinearCmd.Create(this.Index,
157
+ new Array(linearAttrs.length).fill([values, duration]));
158
+ } else if (Array.isArray(values)) {
159
+ msg = Messages.LinearCmd.Create(this.Index, values);
160
+ } else {
161
+ throw new ButtplugDeviceException(
162
+ "SendLinearCmd can only take a number and number, or an array of number/number tuples");
163
+ }
164
+ await this.sendExpectOk(msg);
165
+ }
166
+
167
+ public async sensorRead(sensorIndex: number, sensorType: Messages.SensorType): Promise<number[]> {
168
+ let response = await this.send(new Messages.SensorReadCmd(this.Index, sensorIndex, sensorType));
169
+ switch (response.constructor) {
170
+ case Messages.SensorReading:
171
+ return (response as Messages.SensorReading).Data;
172
+ case Messages.Error:
173
+ throw ButtplugException.FromError(response as Messages.Error);
174
+ default:
175
+ throw new ButtplugMessageException(`Message type ${response.constructor} not handled by sensorRead`);
176
+ }
177
+ }
178
+
179
+ public async battery(): Promise<number> {
180
+ let batteryAttrs = this.AllowedMessages.SensorReadCmd?.filter((x) => x.SensorType == Messages.SensorType.Battery);
181
+ if (!batteryAttrs || batteryAttrs.length == 0) {
182
+ throw new ButtplugDeviceException(`Device ${this.Name} has no Battery capabilities`);
183
+ }
184
+ // Find the battery sensor, we'll need its index.
185
+ let result = await this.sensorRead(batteryAttrs[0].Index, Messages.SensorType.Battery);
186
+ return result[0] / 100.0;
187
+ }
188
+
189
+ public async rssi(): Promise<number> {
190
+ let rssiAttrs = this.AllowedMessages.SensorReadCmd?.filter((x) => x.SensorType == Messages.SensorType.RSSI);
191
+ if (!rssiAttrs || rssiAttrs.length == 0) {
192
+ throw new ButtplugDeviceException(`Device ${this.Name} has no RSSI capabilities`);
193
+ }
194
+ // Find the battery sensor, we'll need its index.
195
+ let result = await this.sensorRead(rssiAttrs[0].Index, Messages.SensorType.RSSI);
196
+ return result[0];
197
+ }
198
+
199
+ public async rawRead(endpoint: string, expectedLength: number, timeout: number): Promise<Uint8Array> {
200
+ if (!this.AllowedMessages.RawReadCmd) {
201
+ throw new ButtplugDeviceException(`Device ${this.Name} has no raw read capabilities`);
202
+ }
203
+ if (this.AllowedMessages.RawReadCmd.Endpoints.indexOf(endpoint) == -1) {
204
+ throw new ButtplugDeviceException(`Device ${this.Name} has no raw readable endpoint ${endpoint}`);
205
+ }
206
+ let response = await this.send(new Messages.RawReadCmd(this.Index, endpoint, expectedLength, timeout));
207
+ switch (response.constructor) {
208
+ case Messages.RawReading:
209
+ return new Uint8Array((response as Messages.RawReading).Data);
210
+ case Messages.Error:
211
+ throw ButtplugException.FromError(response as Messages.Error);
212
+ default:
213
+ throw new ButtplugMessageException(`Message type ${response.constructor} not handled by rawRead`);
214
+ }
215
+ }
216
+
217
+ public async rawWrite(endpoint: string, data: Uint8Array, writeWithResponse: boolean): Promise<void> {
218
+ if (!this.AllowedMessages.RawWriteCmd) {
219
+ throw new ButtplugDeviceException(`Device ${this.Name} has no raw write capabilities`);
220
+ }
221
+ if (this.AllowedMessages.RawWriteCmd.Endpoints.indexOf(endpoint) == -1) {
222
+ throw new ButtplugDeviceException(`Device ${this.Name} has no raw writable endpoint ${endpoint}`);
223
+ }
224
+ await this.sendExpectOk(new Messages.RawWriteCmd(this.Index, endpoint, data, writeWithResponse));
225
+ }
226
+
227
+ public async rawSubscribe(endpoint: string): Promise<void> {
228
+ if (!this.AllowedMessages.RawSubscribeCmd) {
229
+ throw new ButtplugDeviceException(`Device ${this.Name} has no raw subscribe capabilities`);
230
+ }
231
+ if (this.AllowedMessages.RawSubscribeCmd.Endpoints.indexOf(endpoint) == -1) {
232
+ throw new ButtplugDeviceException(`Device ${this.Name} has no raw subscribable endpoint ${endpoint}`);
233
+ }
234
+ await this.sendExpectOk(new Messages.RawSubscribeCmd(this.Index, endpoint));
235
+ }
236
+
237
+ public async rawUnsubscribe(endpoint: string): Promise<void> {
238
+ // This reuses raw subscribe's info.
239
+ if (!this.AllowedMessages.RawSubscribeCmd) {
240
+ throw new ButtplugDeviceException(`Device ${this.Name} has no raw unsubscribe capabilities`);
241
+ }
242
+ if (this.AllowedMessages.RawSubscribeCmd.Endpoints.indexOf(endpoint) == -1) {
243
+ throw new ButtplugDeviceException(`Device ${this.Name} has no raw unsubscribable endpoint ${endpoint}`);
244
+ }
245
+ await this.sendExpectOk(new Messages.RawUnsubscribeCmd(this.Index, endpoint));
246
+ }
247
+
248
+ public async stop(): Promise<void> {
249
+ await this.sendExpectOk(new Messages.StopDeviceCmd(this.Index));
250
+ }
251
+
252
+ public EmitDisconnected() {
253
+ this.emit("deviceremoved");
254
+ }
255
+ }