function getDefaultExportFromCjs (x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } var eventemitter3 = {exports: {}}; var hasRequiredEventemitter3; function requireEventemitter3 () { if (hasRequiredEventemitter3) return eventemitter3.exports; hasRequiredEventemitter3 = 1; (function (module) { var has = Object.prototype.hasOwnProperty , prefix = '~'; /** * Constructor to create a storage for our `EE` objects. * An `Events` instance is a plain object whose properties are event names. * * @constructor * @private */ function Events() {} // // We try to not inherit from `Object.prototype`. In some engines creating an // instance in this way is faster than calling `Object.create(null)` directly. // If `Object.create(null)` is not supported we prefix the event names with a // character to make sure that the built-in object properties are not // overridden or used as an attack vector. // if (Object.create) { Events.prototype = Object.create(null); // // This hack is needed because the `__proto__` property is still inherited in // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5. // if (!new Events().__proto__) prefix = false; } /** * Representation of a single event listener. * * @param {Function} fn The listener function. * @param {*} context The context to invoke the listener with. * @param {Boolean} [once=false] Specify if the listener is a one-time listener. * @constructor * @private */ function EE(fn, context, once) { this.fn = fn; this.context = context; this.once = once || false; } /** * Add a listener for a given event. * * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. * @param {(String|Symbol)} event The event name. * @param {Function} fn The listener function. * @param {*} context The context to invoke the listener with. * @param {Boolean} once Specify if the listener is a one-time listener. * @returns {EventEmitter} * @private */ function addListener(emitter, event, fn, context, once) { if (typeof fn !== 'function') { throw new TypeError('The listener must be a function'); } var listener = new EE(fn, context || emitter, once) , evt = prefix ? prefix + event : event; if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++; else if (!emitter._events[evt].fn) emitter._events[evt].push(listener); else emitter._events[evt] = [emitter._events[evt], listener]; return emitter; } /** * Clear event by name. * * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. * @param {(String|Symbol)} evt The Event name. * @private */ function clearEvent(emitter, evt) { if (--emitter._eventsCount === 0) emitter._events = new Events(); else delete emitter._events[evt]; } /** * Minimal `EventEmitter` interface that is molded against the Node.js * `EventEmitter` interface. * * @constructor * @public */ function EventEmitter() { this._events = new Events(); this._eventsCount = 0; } /** * Return an array listing the events for which the emitter has registered * listeners. * * @returns {Array} * @public */ EventEmitter.prototype.eventNames = function eventNames() { var names = [] , events , name; if (this._eventsCount === 0) return names; for (name in (events = this._events)) { if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); } if (Object.getOwnPropertySymbols) { return names.concat(Object.getOwnPropertySymbols(events)); } return names; }; /** * Return the listeners registered for a given event. * * @param {(String|Symbol)} event The event name. * @returns {Array} The registered listeners. * @public */ EventEmitter.prototype.listeners = function listeners(event) { var evt = prefix ? prefix + event : event , handlers = this._events[evt]; if (!handlers) return []; if (handlers.fn) return [handlers.fn]; for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) { ee[i] = handlers[i].fn; } return ee; }; /** * Return the number of listeners listening to a given event. * * @param {(String|Symbol)} event The event name. * @returns {Number} The number of listeners. * @public */ EventEmitter.prototype.listenerCount = function listenerCount(event) { var evt = prefix ? prefix + event : event , listeners = this._events[evt]; if (!listeners) return 0; if (listeners.fn) return 1; return listeners.length; }; /** * Calls each of the listeners registered for a given event. * * @param {(String|Symbol)} event The event name. * @returns {Boolean} `true` if the event had listeners, else `false`. * @public */ EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return false; var listeners = this._events[evt] , len = arguments.length , args , i; if (listeners.fn) { if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); switch (len) { case 1: return listeners.fn.call(listeners.context), true; case 2: return listeners.fn.call(listeners.context, a1), true; case 3: return listeners.fn.call(listeners.context, a1, a2), true; case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; } for (i = 1, args = new Array(len -1); i < len; i++) { args[i - 1] = arguments[i]; } listeners.fn.apply(listeners.context, args); } else { var length = listeners.length , j; for (i = 0; i < length; i++) { if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true); switch (len) { case 1: listeners[i].fn.call(listeners[i].context); break; case 2: listeners[i].fn.call(listeners[i].context, a1); break; case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; default: if (!args) for (j = 1, args = new Array(len -1); j < len; j++) { args[j - 1] = arguments[j]; } listeners[i].fn.apply(listeners[i].context, args); } } } return true; }; /** * Add a listener for a given event. * * @param {(String|Symbol)} event The event name. * @param {Function} fn The listener function. * @param {*} [context=this] The context to invoke the listener with. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.on = function on(event, fn, context) { return addListener(this, event, fn, context, false); }; /** * Add a one-time listener for a given event. * * @param {(String|Symbol)} event The event name. * @param {Function} fn The listener function. * @param {*} [context=this] The context to invoke the listener with. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.once = function once(event, fn, context) { return addListener(this, event, fn, context, true); }; /** * Remove the listeners of a given event. * * @param {(String|Symbol)} event The event name. * @param {Function} fn Only remove the listeners that match this function. * @param {*} context Only remove the listeners that have this context. * @param {Boolean} once Only remove one-time listeners. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return this; if (!fn) { clearEvent(this, evt); return this; } var listeners = this._events[evt]; if (listeners.fn) { if ( listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context) ) { clearEvent(this, evt); } } else { for (var i = 0, events = [], length = listeners.length; i < length; i++) { if ( listeners[i].fn !== fn || (once && !listeners[i].once) || (context && listeners[i].context !== context) ) { events.push(listeners[i]); } } // // Reset the array, or remove it completely if we have no more listeners. // if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; else clearEvent(this, evt); } return this; }; /** * Remove all listeners, or those of the specified event. * * @param {(String|Symbol)} [event] The event name. * @returns {EventEmitter} `this`. * @public */ EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) { var evt; if (event) { evt = prefix ? prefix + event : event; if (this._events[evt]) clearEvent(this, evt); } else { this._events = new Events(); this._eventsCount = 0; } return this; }; // // Alias methods names because people roll like that. // EventEmitter.prototype.off = EventEmitter.prototype.removeListener; EventEmitter.prototype.addListener = EventEmitter.prototype.on; // // Expose the prefix. // EventEmitter.prefixed = prefix; // // Allow `EventEmitter` to be imported as module namespace. // EventEmitter.EventEmitter = EventEmitter; // // Expose the module. // { module.exports = EventEmitter; } } (eventemitter3)); return eventemitter3.exports; } var eventemitter3Exports = requireEventemitter3(); const EventEmitter = /*@__PURE__*/getDefaultExportFromCjs(eventemitter3Exports); var ButtplugLogLevel = /* @__PURE__ */ ((ButtplugLogLevel2) => { ButtplugLogLevel2[ButtplugLogLevel2["Off"] = 0] = "Off"; ButtplugLogLevel2[ButtplugLogLevel2["Error"] = 1] = "Error"; ButtplugLogLevel2[ButtplugLogLevel2["Warn"] = 2] = "Warn"; ButtplugLogLevel2[ButtplugLogLevel2["Info"] = 3] = "Info"; ButtplugLogLevel2[ButtplugLogLevel2["Debug"] = 4] = "Debug"; ButtplugLogLevel2[ButtplugLogLevel2["Trace"] = 5] = "Trace"; return ButtplugLogLevel2; })(ButtplugLogLevel || {}); class LogMessage { /** Timestamp for the log message */ timestamp; /** Log Message */ logMessage; /** Log Level */ logLevel; /** * @param logMessage Log message. * @param logLevel: Log severity level. */ constructor(logMessage, logLevel) { const a = /* @__PURE__ */ new Date(); const hour = a.getHours(); const min = a.getMinutes(); const sec = a.getSeconds(); this.timestamp = `${hour}:${min}:${sec}`; this.logMessage = logMessage; this.logLevel = logLevel; } /** * Returns the log message. */ get Message() { return this.logMessage; } /** * Returns the log message level. */ get LogLevel() { return this.logLevel; } /** * Returns the log message timestamp. */ get Timestamp() { return this.timestamp; } /** * Returns a formatted string with timestamp, level, and message. */ get FormattedMessage() { return `${ButtplugLogLevel[this.logLevel]} : ${this.timestamp} : ${this.logMessage}`; } } class ButtplugLogger extends EventEmitter { /** Singleton instance for the logger */ static sLogger = void 0; /** Sets maximum log level to log to console */ maximumConsoleLogLevel = 0 /* Off */; /** Sets maximum log level for all log messages */ maximumEventLogLevel = 0 /* Off */; /** * Returns the stored static instance of the logger, creating one if it * doesn't currently exist. */ static get Logger() { if (ButtplugLogger.sLogger === void 0) { ButtplugLogger.sLogger = new ButtplugLogger(); } return this.sLogger; } /** * Constructor. Can only be called internally since we regulate ButtplugLogger * ownership. */ constructor() { super(); } /** * Set the maximum log level to output to console. */ get MaximumConsoleLogLevel() { return this.maximumConsoleLogLevel; } /** * Get the maximum log level to output to console. */ set MaximumConsoleLogLevel(buttplugLogLevel) { this.maximumConsoleLogLevel = buttplugLogLevel; } /** * Set the global maximum log level */ get MaximumEventLogLevel() { return this.maximumEventLogLevel; } /** * Get the global maximum log level */ set MaximumEventLogLevel(logLevel) { this.maximumEventLogLevel = logLevel; } /** * Log new message at Error level. */ Error(msg) { this.AddLogMessage(msg, 1 /* Error */); } /** * Log new message at Warn level. */ Warn(msg) { this.AddLogMessage(msg, 2 /* Warn */); } /** * Log new message at Info level. */ Info(msg) { this.AddLogMessage(msg, 3 /* Info */); } /** * Log new message at Debug level. */ Debug(msg) { this.AddLogMessage(msg, 4 /* Debug */); } /** * Log new message at Trace level. */ Trace(msg) { this.AddLogMessage(msg, 5 /* Trace */); } /** * Checks to see if message should be logged, and if so, adds message to the * log buffer. May also print message and emit event. */ AddLogMessage(msg, level) { if (level > this.maximumEventLogLevel && level > this.maximumConsoleLogLevel) { return; } const logMsg = new LogMessage(msg, level); if (level <= this.maximumConsoleLogLevel) { console.log(logMsg.FormattedMessage); } if (level <= this.maximumEventLogLevel) { this.emit("log", logMsg); } } } class ButtplugError extends Error { get ErrorClass() { return this.errorClass; } get InnerError() { return this.innerError; } get Id() { return this.messageId; } get ErrorMessage() { return { Error: { Id: this.Id, ErrorCode: this.ErrorClass, ErrorMessage: this.message } }; } static LogAndError(constructor, logger, message, id = SYSTEM_MESSAGE_ID) { logger.Error(message); return new constructor(message, id); } static FromError(error) { switch (error.ErrorCode) { case ErrorClass.ERROR_DEVICE: return new ButtplugDeviceError(error.ErrorMessage, error.Id); case ErrorClass.ERROR_INIT: return new ButtplugInitError(error.ErrorMessage, error.Id); case ErrorClass.ERROR_UNKNOWN: return new ButtplugUnknownError(error.ErrorMessage, error.Id); case ErrorClass.ERROR_PING: return new ButtplugPingError(error.ErrorMessage, error.Id); case ErrorClass.ERROR_MSG: return new ButtplugMessageError(error.ErrorMessage, error.Id); default: throw new Error(`Message type ${error.ErrorCode} not handled`); } } errorClass = ErrorClass.ERROR_UNKNOWN; innerError; messageId; constructor(message, errorClass, id = SYSTEM_MESSAGE_ID, inner) { super(message); this.errorClass = errorClass; this.innerError = inner; this.messageId = id; } } class ButtplugInitError extends ButtplugError { constructor(message, id = SYSTEM_MESSAGE_ID) { super(message, ErrorClass.ERROR_INIT, id); } } class ButtplugDeviceError extends ButtplugError { constructor(message, id = SYSTEM_MESSAGE_ID) { super(message, ErrorClass.ERROR_DEVICE, id); } } class ButtplugMessageError extends ButtplugError { constructor(message, id = SYSTEM_MESSAGE_ID) { super(message, ErrorClass.ERROR_MSG, id); } } class ButtplugPingError extends ButtplugError { constructor(message, id = SYSTEM_MESSAGE_ID) { super(message, ErrorClass.ERROR_PING, id); } } class ButtplugUnknownError extends ButtplugError { constructor(message, id = SYSTEM_MESSAGE_ID) { super(message, ErrorClass.ERROR_UNKNOWN, id); } } const SYSTEM_MESSAGE_ID = 0; const DEFAULT_MESSAGE_ID = 1; const MAX_ID = 4294967295; const MESSAGE_SPEC_VERSION_MAJOR = 4; const MESSAGE_SPEC_VERSION_MINOR = 0; function msgId(msg) { for (const [_, entry] of Object.entries(msg)) { if (entry != void 0) { return entry.Id; } } throw new ButtplugMessageError(`Message ${msg} does not have an ID.`); } function setMsgId(msg, id) { for (const [_, entry] of Object.entries(msg)) { if (entry != void 0) { entry.Id = id; return; } } throw new ButtplugMessageError(`Message ${msg} does not have an ID.`); } var ErrorClass = /* @__PURE__ */ ((ErrorClass2) => { ErrorClass2[ErrorClass2["ERROR_UNKNOWN"] = 0] = "ERROR_UNKNOWN"; ErrorClass2[ErrorClass2["ERROR_INIT"] = 1] = "ERROR_INIT"; ErrorClass2[ErrorClass2["ERROR_PING"] = 2] = "ERROR_PING"; ErrorClass2[ErrorClass2["ERROR_MSG"] = 3] = "ERROR_MSG"; ErrorClass2[ErrorClass2["ERROR_DEVICE"] = 4] = "ERROR_DEVICE"; return ErrorClass2; })(ErrorClass || {}); var OutputType = /* @__PURE__ */ ((OutputType2) => { OutputType2["Unknown"] = "Unknown"; OutputType2["Vibrate"] = "Vibrate"; OutputType2["Rotate"] = "Rotate"; OutputType2["Oscillate"] = "Oscillate"; OutputType2["Constrict"] = "Constrict"; OutputType2["Inflate"] = "Inflate"; OutputType2["Position"] = "Position"; OutputType2["HwPositionWithDuration"] = "HwPositionWithDuration"; OutputType2["Temperature"] = "Temperature"; OutputType2["Spray"] = "Spray"; OutputType2["Led"] = "Led"; return OutputType2; })(OutputType || {}); var InputType = /* @__PURE__ */ ((InputType2) => { InputType2["Unknown"] = "Unknown"; InputType2["Battery"] = "Battery"; InputType2["RSSI"] = "RSSI"; InputType2["Button"] = "Button"; InputType2["Pressure"] = "Pressure"; return InputType2; })(InputType || {}); var InputCommandType = /* @__PURE__ */ ((InputCommandType2) => { InputCommandType2["Read"] = "Read"; InputCommandType2["Subscribe"] = "Subscribe"; InputCommandType2["Unsubscribe"] = "Unsubscribe"; return InputCommandType2; })(InputCommandType || {}); class ButtplugClientDeviceFeature { constructor(_deviceIndex, _deviceName, _feature, _sendClosure) { this._deviceIndex = _deviceIndex; this._deviceName = _deviceName; this._feature = _feature; this._sendClosure = _sendClosure; } send = async (msg) => { return await this._sendClosure(msg); }; sendMsgExpectOk = async (msg) => { const response = await this.send(msg); if (response.Ok !== void 0) { return; } else if (response.Error !== void 0) { throw ButtplugError.FromError(response); } else { throw new ButtplugMessageError("Expected Ok or Error, and didn't get either!"); } }; isOutputValid(type) { if (this._feature.Output !== void 0 && !Object.prototype.hasOwnProperty.call(this._feature.Output, type)) { throw new ButtplugDeviceError( `Feature index ${this._feature.FeatureIndex} does not support type ${type} for device ${this._deviceName}` ); } } isInputValid(type) { if (this._feature.Input !== void 0 && !Object.prototype.hasOwnProperty.call(this._feature.Input, type)) { throw new ButtplugDeviceError( `Feature index ${this._feature.FeatureIndex} does not support type ${type} for device ${this._deviceName}` ); } } async sendOutputCmd(command) { this.isOutputValid(command.outputType); if (command.value === void 0) { throw new ButtplugDeviceError(`${command.outputType} requires value defined`); } const type = command.outputType; let duration = void 0; if (type == OutputType.HwPositionWithDuration) { if (command.duration === void 0) { throw new ButtplugDeviceError("PositionWithDuration requires duration defined"); } duration = command.duration; } let value; const p = command.value; if (p.percent === void 0) { value = command.value.steps; } else { value = Math.ceil(this._feature.Output[type].Value[1] * p.percent); } const newCommand = { Value: value, Duration: duration }; const outCommand = {}; outCommand[type.toString()] = newCommand; const cmd = { OutputCmd: { Id: 1, DeviceIndex: this._deviceIndex, FeatureIndex: this._feature.FeatureIndex, Command: outCommand } }; await this.sendMsgExpectOk(cmd); } get featureDescriptor() { return this._feature.FeatureDescription; } get featureIndex() { return this._feature.FeatureIndex; } get outputTypes() { if (this._feature.Output === void 0) return []; return Object.keys(this._feature.Output); } get inputTypes() { if (this._feature.Input === void 0) return []; return Object.keys(this._feature.Input); } outputMaxValue(type) { if (this._feature.Output === void 0 || this._feature.Output[type] === void 0) { return 0; } const val = this._feature.Output[type].Value; if (Array.isArray(val)) { return val[val.length - 1]; } return val; } hasOutput(type) { if (this._feature.Output !== void 0) { return Object.prototype.hasOwnProperty.call(this._feature.Output, type.toString()); } return false; } hasInput(type) { if (this._feature.Input !== void 0) { return Object.prototype.hasOwnProperty.call(this._feature.Input, type.toString()); } return false; } async runOutput(cmd) { if (this._feature.Output !== void 0 && Object.prototype.hasOwnProperty.call(this._feature.Output, cmd.outputType.toString())) { return this.sendOutputCmd(cmd); } throw new ButtplugDeviceError(`Output type ${cmd.outputType} not supported by feature.`); } async runInput(inputType, inputCommand) { this.isInputValid(inputType); const inputAttributes = this._feature.Input[inputType]; console.log(this._feature.Input); if (inputCommand === InputCommandType.Unsubscribe && !inputAttributes.Command.includes(InputCommandType.Subscribe) && !inputAttributes.Command.includes(inputCommand)) { throw new ButtplugDeviceError(`${inputType} does not support command ${inputCommand}`); } const cmd = { InputCmd: { Id: 1, DeviceIndex: this._deviceIndex, FeatureIndex: this._feature.FeatureIndex, Type: inputType, Command: inputCommand } }; if (inputCommand == InputCommandType.Read) { const response = await this.send(cmd); if (response.InputReading !== void 0) { return response.InputReading; } else if (response.Error !== void 0) { throw ButtplugError.FromError(response); } else { throw new ButtplugMessageError("Expected InputReading or Error, and didn't get either!"); } } else { console.log(`Sending subscribe message: ${JSON.stringify(cmd)}`); await this.sendMsgExpectOk(cmd); console.log("Got back ok?"); } } } class ButtplugClientDevice extends EventEmitter { /** * @param _index Index of the device, as created by the device manager. * @param _name Name of the device. * @param allowedMsgs Buttplug messages the device can receive. */ constructor(_deviceInfo, _sendClosure) { super(); this._deviceInfo = _deviceInfo; this._sendClosure = _sendClosure; this._features = new Map( Object.entries(_deviceInfo.DeviceFeatures).map(([index, v]) => [ parseInt(index), new ButtplugClientDeviceFeature( _deviceInfo.DeviceIndex, _deviceInfo.DeviceName, v, _sendClosure ) ]) ); } _features; /** * Return the name of the device. */ get name() { return this._deviceInfo.DeviceName; } /** * Return the user set name of the device. */ get displayName() { return this._deviceInfo.DeviceDisplayName; } /** * Return the index of the device. */ get index() { return this._deviceInfo.DeviceIndex; } /** * Return the index of the device. */ get messageTimingGap() { return this._deviceInfo.DeviceMessageTimingGap; } get features() { return this._features; } static fromMsg(msg, sendClosure) { return new ButtplugClientDevice(msg, sendClosure); } async send(msg) { return await this._sendClosure(msg); } sendMsgExpectOk = async (msg) => { const response = await this.send(msg); if (response.Ok !== void 0) { return; } else if (response.Error !== void 0) { throw ButtplugError.FromError(response); } else ; }; isOutputValid(featureIndex, type) { if (!Object.prototype.hasOwnProperty.call( this._deviceInfo.DeviceFeatures, featureIndex.toString() )) { throw new ButtplugDeviceError( `Feature index ${featureIndex} does not exist for device ${this.name}` ); } if (this._deviceInfo.DeviceFeatures[featureIndex.toString()].Outputs !== void 0 && !Object.prototype.hasOwnProperty.call( this._deviceInfo.DeviceFeatures[featureIndex.toString()].Outputs, type )) { throw new ButtplugDeviceError( `Feature index ${featureIndex} does not support type ${type} for device ${this.name}` ); } } hasOutput(type) { return this._features.values().filter((f) => f.hasOutput(type)).toArray().length > 0; } hasInput(type) { return this._features.values().filter((f) => f.hasInput(type)).toArray().length > 0; } async runOutput(cmd) { const p = []; for (const f of this._features.values()) { if (f.hasOutput(cmd.outputType)) { p.push(f.runOutput(cmd)); } } if (p.length == 0) { return Promise.reject(`No features with output type ${cmd.outputType}`); } await Promise.all(p); } async stop() { await this.sendMsgExpectOk({ StopCmd: { Id: 1, DeviceIndex: this.index, FeatureIndex: void 0, Inputs: true, Outputs: true } }); } async battery() { for (const f of this._features.values()) { if (f.hasInput(InputType.Battery)) { const response = await f.runInput( InputType.Battery, InputCommandType.Read ); if (response === void 0) { throw new ButtplugMessageError("Got incorrect message back."); } if (response.Reading[InputType.Battery] === void 0) { throw new ButtplugMessageError("Got reading with no Battery info."); } return response.Reading[InputType.Battery].Value; } } throw new ButtplugDeviceError(`No battery present on this device.`); } emitDisconnected() { this.emit("deviceremoved"); } } class ButtplugMessageSorter { constructor(_useCounter) { this._useCounter = _useCounter; } _counter = 1; _waitingMsgs = /* @__PURE__ */ new Map(); // One of the places we should actually return a promise, as we need to store // them while waiting for them to return across the line. // tslint:disable:promise-function-async PrepareOutgoingMessage(msg) { if (this._useCounter) { setMsgId(msg, this._counter); this._counter += 1; } let res; let rej; const msgPromise = new Promise((resolve, reject) => { res = resolve; rej = reject; }); this._waitingMsgs.set(msgId(msg), [res, rej]); return msgPromise; } ParseIncomingMessages(msgs) { const noMatch = []; for (const x of msgs) { const id = msgId(x); if (id !== SYSTEM_MESSAGE_ID && this._waitingMsgs.has(id)) { const [res, rej] = this._waitingMsgs.get(id); this._waitingMsgs.delete(id); if (x.Error !== void 0) { rej(ButtplugError.FromError(x.Error)); continue; } res(x); continue; } else { noMatch.push(x); } } return noMatch; } } class ButtplugClientConnectorException extends ButtplugError { constructor(message) { super(message, ErrorClass.ERROR_UNKNOWN); } } class ButtplugClient extends EventEmitter { _pingTimer = null; _connector = null; _devices = /* @__PURE__ */ new Map(); _clientName; _logger = ButtplugLogger.Logger; _isScanning = false; _sorter = new ButtplugMessageSorter(true); constructor(clientName = "Generic Buttplug Client") { super(); this._clientName = clientName; this._logger.Debug(`ButtplugClient: Client ${clientName} created.`); } get connected() { return this._connector !== null && this._connector.Connected; } get devices() { this.checkConnector(); return this._devices; } get isScanning() { return this._isScanning; } connect = async (connector) => { this._logger.Info(`ButtplugClient: Connecting using ${connector.constructor.name}`); await connector.connect(); this._connector = connector; this._connector.addListener("message", this.parseMessages); this._connector.addListener("disconnect", this.disconnectHandler); await this.initializeConnection(); }; disconnect = async () => { this._logger.Debug("ButtplugClient: Disconnect called"); this._devices.clear(); this.checkConnector(); await this.shutdownConnection(); await this._connector.disconnect(); }; startScanning = async () => { this._logger.Debug("ButtplugClient: StartScanning called"); this._isScanning = true; await this.sendMsgExpectOk({ StartScanning: { Id: 1 } }); }; stopScanning = async () => { this._logger.Debug("ButtplugClient: StopScanning called"); this._isScanning = false; await this.sendMsgExpectOk({ StopScanning: { Id: 1 } }); }; stopAllDevices = async () => { this._logger.Debug("ButtplugClient: StopAllDevices"); await this.sendMsgExpectOk({ StopCmd: { Id: 1, DeviceIndex: void 0, FeatureIndex: void 0, Inputs: true, Outputs: true } }); }; disconnectHandler = () => { this._logger.Info("ButtplugClient: Disconnect event receieved."); this.emit("disconnect"); }; parseMessages = (msgs) => { const leftoverMsgs = this._sorter.ParseIncomingMessages(msgs); for (const x of leftoverMsgs) { if (x.DeviceList !== void 0) { this.parseDeviceList(x.DeviceList); break; } else if (x.ScanningFinished !== void 0) { this._isScanning = false; this.emit("scanningfinished", x); } else if (x.InputReading !== void 0) { this.emit("inputreading", x); } else { console.log(`Unhandled message: ${x}`); } } }; initializeConnection = async () => { this.checkConnector(); const msg = await this.sendMessage({ RequestServerInfo: { ClientName: this._clientName, Id: 1, ProtocolVersionMajor: MESSAGE_SPEC_VERSION_MAJOR, ProtocolVersionMinor: MESSAGE_SPEC_VERSION_MINOR } }); if (msg.ServerInfo !== void 0) { const serverinfo = msg; this._logger.Info(`ButtplugClient: Connected to Server ${serverinfo.ServerName}`); serverinfo.MaxPingTime; await this.requestDeviceList(); return true; } else if (msg.Error !== void 0) { await this._connector.disconnect(); const err = msg.Error; throw ButtplugError.LogAndError( ButtplugInitError, this._logger, `Cannot connect to server. ${err.ErrorMessage}` ); } return false; }; parseDeviceList = (list) => { for (const [_, d] of Object.entries(list.Devices)) { if (!this._devices.has(d.DeviceIndex)) { const device = ButtplugClientDevice.fromMsg(d, this.sendMessageClosure); this._logger.Debug(`ButtplugClient: Adding Device: ${device}`); this._devices.set(d.DeviceIndex, device); this.emit("deviceadded", device); } else { this._logger.Debug(`ButtplugClient: Device already added: ${d}`); } } for (const [index, device] of this._devices.entries()) { if (!Object.prototype.hasOwnProperty.call(list.Devices, index.toString())) { this._devices.delete(index); this.emit("deviceremoved", device); } } }; requestDeviceList = async () => { this.checkConnector(); this._logger.Debug("ButtplugClient: ReceiveDeviceList called"); const response = await this.sendMessage({ RequestDeviceList: { Id: 1 } }); this.parseDeviceList(response.DeviceList); }; shutdownConnection = async () => { await this.stopAllDevices(); if (this._pingTimer !== null) { clearInterval(this._pingTimer); this._pingTimer = null; } }; async sendMessage(msg) { this.checkConnector(); const p = this._sorter.PrepareOutgoingMessage(msg); await this._connector.send(msg); return await p; } checkConnector() { if (!this.connected) { throw new ButtplugClientConnectorException("ButtplugClient not connected"); } } sendMsgExpectOk = async (msg) => { const response = await this.sendMessage(msg); if (response.Ok !== void 0) { return; } else if (response.Error !== void 0) { throw ButtplugError.FromError(response); } else { throw ButtplugError.LogAndError( ButtplugMessageError, this._logger, `Message ${response} not handled by SendMsgExpectOk` ); } }; sendMessageClosure = async (msg) => { return await this.sendMessage(msg); }; } class ButtplugBrowserWebsocketConnector extends EventEmitter { constructor(_url) { super(); this._url = _url; } _ws; _websocketConstructor = null; get Connected() { return this._ws !== void 0; } connect = async () => { return new Promise((resolve, reject) => { const ws = new (this._websocketConstructor ?? WebSocket)(this._url); const onErrorCallback = (event) => { reject(event); }; const onCloseCallback = (event) => reject(event.reason); ws.addEventListener("open", async () => { this._ws = ws; try { await this.initialize(); this._ws.addEventListener("message", (msg) => { this.parseIncomingMessage(msg); }); this._ws.removeEventListener("close", onCloseCallback); this._ws.removeEventListener("error", onErrorCallback); this._ws.addEventListener("close", this.disconnect); resolve(); } catch (e) { reject(e); } }); ws.addEventListener("error", onErrorCallback); ws.addEventListener("close", onCloseCallback); }); }; disconnect = async () => { if (!this.Connected) { return; } this._ws.close(); this._ws = void 0; this.emit("disconnect"); }; sendMessage(msg) { if (!this.Connected) { throw new Error("ButtplugBrowserWebsocketConnector not connected"); } this._ws.send("[" + JSON.stringify(msg) + "]"); } initialize = async () => { return Promise.resolve(); }; parseIncomingMessage(event) { if (typeof event.data === "string") { const msgs = JSON.parse(event.data); this.emit("message", msgs); } else if (event.data instanceof Blob) ; } onReaderLoad(event) { const msgs = JSON.parse(event.target.result); this.emit("message", msgs); } } class ButtplugBrowserWebsocketClientConnector extends ButtplugBrowserWebsocketConnector { send = (msg) => { if (!this.Connected) { throw new Error("ButtplugClient not connected"); } this.sendMessage(msg); }; } var browser; var hasRequiredBrowser; function requireBrowser () { if (hasRequiredBrowser) return browser; hasRequiredBrowser = 1; browser = function () { throw new Error( 'ws does not work in the browser. Browser clients must use the native ' + 'WebSocket object' ); }; return browser; } var browserExports = requireBrowser(); class ButtplugNodeWebsocketClientConnector extends ButtplugBrowserWebsocketClientConnector { _websocketConstructor = browserExports.WebSocket; } class PercentOrSteps { _percent; _steps; get percent() { return this._percent; } get steps() { return this._steps; } static createSteps(s) { const v = new PercentOrSteps(); v._steps = s; return v; } static createPercent(p) { if (p < 0 || p > 1) { throw new ButtplugDeviceError(`Percent value ${p} is not in the range 0.0 <= x <= 1.0`); } const v = new PercentOrSteps(); v._percent = p; return v; } } class DeviceOutputCommand { constructor(_outputType, _value, _duration) { this._outputType = _outputType; this._value = _value; this._duration = _duration; } get outputType() { return this._outputType; } get value() { return this._value; } get duration() { return this._duration; } } class DeviceOutputValueConstructor { constructor(_outputType) { this._outputType = _outputType; } steps(steps) { return new DeviceOutputCommand(this._outputType, PercentOrSteps.createSteps(steps), void 0); } percent(percent) { return new DeviceOutputCommand( this._outputType, PercentOrSteps.createPercent(percent), void 0 ); } } class DeviceOutputPositionWithDurationConstructor { steps(steps, duration) { return new DeviceOutputCommand( OutputType.Position, PercentOrSteps.createSteps(steps), duration ); } percent(percent, duration) { return new DeviceOutputCommand( OutputType.HwPositionWithDuration, PercentOrSteps.createPercent(percent), duration ); } } class DeviceOutput { constructor() { } static get Vibrate() { return new DeviceOutputValueConstructor(OutputType.Vibrate); } static get Rotate() { return new DeviceOutputValueConstructor(OutputType.Rotate); } static get Oscillate() { return new DeviceOutputValueConstructor(OutputType.Oscillate); } static get Constrict() { return new DeviceOutputValueConstructor(OutputType.Constrict); } static get Inflate() { return new DeviceOutputValueConstructor(OutputType.Inflate); } static get Temperature() { return new DeviceOutputValueConstructor(OutputType.Temperature); } static get Led() { return new DeviceOutputValueConstructor(OutputType.Led); } static get Spray() { return new DeviceOutputValueConstructor(OutputType.Spray); } static get Position() { return new DeviceOutputValueConstructor(OutputType.Position); } static get PositionWithDuration() { return new DeviceOutputPositionWithDurationConstructor(); } } class ButtplugWasmClientConnector extends EventEmitter { static _loggingActivated = false; static wasmInstance; _connected = false; client; serverPtr; constructor() { super(); } get Connected() { return this._connected; } static maybeLoadWasm = async () => { if (ButtplugWasmClientConnector.wasmInstance == void 0) { const wasmModule = await import('../wasm/index.js'); await wasmModule.default(); ButtplugWasmClientConnector.wasmInstance = wasmModule; } }; static activateLogging = async (logLevel = "debug") => { await ButtplugWasmClientConnector.maybeLoadWasm(); if (this._loggingActivated) { console.log("Logging already activated, ignoring."); return; } console.log("Turning on logging."); ButtplugWasmClientConnector.wasmInstance.buttplug_activate_env_logger(logLevel); }; initialize = async () => { }; connect = async () => { await ButtplugWasmClientConnector.maybeLoadWasm(); this.client = ButtplugWasmClientConnector.wasmInstance.buttplug_create_embedded_wasm_server( (msgs) => { this.emitMessage(msgs); }, this.serverPtr ); this._connected = true; }; disconnect = async () => { }; send = (msg) => { ButtplugWasmClientConnector.wasmInstance.buttplug_client_send_json_message( this.client, new TextEncoder().encode("[" + JSON.stringify(msg) + "]"), (output) => { this.emitMessage(output); } ); }; emitMessage = (msg) => { const str = new TextDecoder().decode(msg); const msgs = JSON.parse(str); this.emit("message", msgs); }; } export { ButtplugBrowserWebsocketClientConnector, ButtplugClient, ButtplugClientConnectorException, ButtplugClientDevice, ButtplugClientDeviceFeature, ButtplugDeviceError, ButtplugError, ButtplugInitError, ButtplugLogLevel, ButtplugLogger, ButtplugMessageError, ButtplugMessageSorter, ButtplugNodeWebsocketClientConnector, ButtplugPingError, ButtplugUnknownError, ButtplugWasmClientConnector, DEFAULT_MESSAGE_ID, DeviceOutput, DeviceOutputCommand, DeviceOutputPositionWithDurationConstructor, DeviceOutputValueConstructor, ErrorClass, InputCommandType, InputType, LogMessage, MAX_ID, MESSAGE_SPEC_VERSION_MAJOR, MESSAGE_SPEC_VERSION_MINOR, OutputType, SYSTEM_MESSAGE_ID, msgId, setMsgId };