/*! * Buttplug JS Source Code File - Visit https://buttplug.io for more info about * the project. Licensed under the BSD 3-Clause license. See LICENSE file in the * project root for full license information. * * @copyright Copyright (c) Nonpolynomial Labs LLC. All rights reserved. */ // tslint:disable:max-classes-per-file "use strict"; import { instanceToPlain, Type } from "class-transformer"; import "reflect-metadata"; export const SYSTEM_MESSAGE_ID = 0; export const DEFAULT_MESSAGE_ID = 1; export const MAX_ID = 4294967295; export const MESSAGE_SPEC_VERSION = 3; export class MessageAttributes { public ScalarCmd?: Array; public RotateCmd?: Array; public LinearCmd?: Array; public RawReadCmd?: RawDeviceMessageAttributes; public RawWriteCmd?: RawDeviceMessageAttributes; public RawSubscribeCmd?: RawDeviceMessageAttributes; public SensorReadCmd?: Array; public SensorSubscribeCmd?: Array; public StopDeviceCmd: {}; constructor(data: Partial) { Object.assign(this, data); } public update() { this.ScalarCmd?.forEach((x, i) => (x.Index = i)); this.RotateCmd?.forEach((x, i) => (x.Index = i)); this.LinearCmd?.forEach((x, i) => (x.Index = i)); this.SensorReadCmd?.forEach((x, i) => (x.Index = i)); this.SensorSubscribeCmd?.forEach((x, i) => (x.Index = i)); } } export enum ActuatorType { Unknown = "Unknown", Vibrate = "Vibrate", Rotate = "Rotate", Oscillate = "Oscillate", Constrict = "Constrict", Inflate = "Inflate", Position = "Position", } export enum SensorType { Unknown = "Unknown", Battery = "Battery", RSSI = "RSSI", Button = "Button", Pressure = "Pressure", // Temperature, // Accelerometer, // Gyro, } export class GenericDeviceMessageAttributes { public FeatureDescriptor: string; public ActuatorType: ActuatorType; public StepCount: number; public Index = 0; constructor(data: Partial) { Object.assign(this, data); } } export class RawDeviceMessageAttributes { constructor(public Endpoints: Array) {} } export class SensorDeviceMessageAttributes { public FeatureDescriptor: string; public SensorType: SensorType; public StepRange: Array; public Index = 0; constructor(data: Partial) { Object.assign(this, data); } } export abstract class ButtplugMessage { constructor(public Id: number) {} // tslint:disable-next-line:ban-types public get Type(): Function { return this.constructor; } public toJSON(): string { return JSON.stringify(this.toProtocolFormat()); } public toProtocolFormat(): object { const jsonObj = {}; jsonObj[(this.constructor as unknown as { Name: string }).Name] = instanceToPlain(this); return jsonObj; } public update() {} } export abstract class ButtplugDeviceMessage extends ButtplugMessage { constructor( public DeviceIndex: number, public Id: number, ) { super(Id); } } export abstract class ButtplugSystemMessage extends ButtplugMessage { constructor(public Id: number = SYSTEM_MESSAGE_ID) { super(Id); } } export class Ok extends ButtplugSystemMessage { static Name = "Ok"; constructor(public Id: number = DEFAULT_MESSAGE_ID) { super(Id); } } export class Ping extends ButtplugMessage { static Name = "Ping"; constructor(public Id: number = DEFAULT_MESSAGE_ID) { super(Id); } } export enum ErrorClass { ERROR_UNKNOWN, ERROR_INIT, ERROR_PING, ERROR_MSG, ERROR_DEVICE, } export class Error extends ButtplugMessage { static Name = "Error"; constructor( public ErrorMessage: string, public ErrorCode: ErrorClass = ErrorClass.ERROR_UNKNOWN, public Id: number = DEFAULT_MESSAGE_ID, ) { super(Id); } get Schemversion() { return 0; } } export class DeviceInfo { public DeviceIndex: number; public DeviceName: string; @Type(() => MessageAttributes) public DeviceMessages: MessageAttributes; public DeviceDisplayName?: string; public DeviceMessageTimingGap?: number; constructor(data: Partial) { Object.assign(this, data); } } export class DeviceList extends ButtplugMessage { static Name = "DeviceList"; @Type(() => DeviceInfo) public Devices: DeviceInfo[]; public Id: number; constructor(devices: DeviceInfo[], id: number = DEFAULT_MESSAGE_ID) { super(id); this.Devices = devices; this.Id = id; } public update() { for (const device of this.Devices) { device.DeviceMessages.update(); } } } export class DeviceAdded extends ButtplugSystemMessage { static Name = "DeviceAdded"; public DeviceIndex: number; public DeviceName: string; @Type(() => MessageAttributes) public DeviceMessages: MessageAttributes; public DeviceDisplayName?: string; public DeviceMessageTimingGap?: number; constructor(data: Partial) { super(); Object.assign(this, data); } public update() { this.DeviceMessages.update(); } } export class DeviceRemoved extends ButtplugSystemMessage { static Name = "DeviceRemoved"; constructor(public DeviceIndex: number) { super(); } } export class RequestDeviceList extends ButtplugMessage { static Name = "RequestDeviceList"; constructor(public Id: number = DEFAULT_MESSAGE_ID) { super(Id); } } export class StartScanning extends ButtplugMessage { static Name = "StartScanning"; constructor(public Id: number = DEFAULT_MESSAGE_ID) { super(Id); } } export class StopScanning extends ButtplugMessage { static Name = "StopScanning"; constructor(public Id: number = DEFAULT_MESSAGE_ID) { super(Id); } } export class ScanningFinished extends ButtplugSystemMessage { static Name = "ScanningFinished"; constructor() { super(); } } export class RequestServerInfo extends ButtplugMessage { static Name = "RequestServerInfo"; constructor( public ClientName: string, public MessageVersion: number = 0, public Id: number = DEFAULT_MESSAGE_ID, ) { super(Id); } } export class ServerInfo extends ButtplugSystemMessage { static Name = "ServerInfo"; constructor( public MessageVersion: number, public MaxPingTime: number, public ServerName: string, public Id: number = DEFAULT_MESSAGE_ID, ) { super(); } } export class StopDeviceCmd extends ButtplugDeviceMessage { static Name = "StopDeviceCmd"; constructor( public DeviceIndex: number = -1, public Id: number = DEFAULT_MESSAGE_ID, ) { super(DeviceIndex, Id); } } export class StopAllDevices extends ButtplugMessage { static Name = "StopAllDevices"; constructor(public Id: number = DEFAULT_MESSAGE_ID) { super(Id); } } export class GenericMessageSubcommand { protected constructor(public Index: number) {} } export class ScalarSubcommand extends GenericMessageSubcommand { constructor( Index: number, public Scalar: number, public ActuatorType: ActuatorType, ) { super(Index); } } export class ScalarCmd extends ButtplugDeviceMessage { static Name = "ScalarCmd"; constructor( public Scalars: ScalarSubcommand[], public DeviceIndex: number = -1, public Id: number = DEFAULT_MESSAGE_ID, ) { super(DeviceIndex, Id); } } export class RotateSubcommand extends GenericMessageSubcommand { constructor( Index: number, public Speed: number, public Clockwise: boolean, ) { super(Index); } } export class RotateCmd extends ButtplugDeviceMessage { static Name = "RotateCmd"; public static Create( deviceIndex: number, commands: [number, boolean][], ): RotateCmd { const cmdList: RotateSubcommand[] = new Array(); let i = 0; for (const [speed, clockwise] of commands) { cmdList.push(new RotateSubcommand(i, speed, clockwise)); ++i; } return new RotateCmd(cmdList, deviceIndex); } constructor( public Rotations: RotateSubcommand[], public DeviceIndex: number = -1, public Id: number = DEFAULT_MESSAGE_ID, ) { super(DeviceIndex, Id); } } export class VectorSubcommand extends GenericMessageSubcommand { constructor( Index: number, public Position: number, public Duration: number, ) { super(Index); } } export class LinearCmd extends ButtplugDeviceMessage { static Name = "LinearCmd"; public static Create( deviceIndex: number, commands: [number, number][], ): LinearCmd { const cmdList: VectorSubcommand[] = new Array(); let i = 0; for (const cmd of commands) { cmdList.push(new VectorSubcommand(i, cmd[0], cmd[1])); ++i; } return new LinearCmd(cmdList, deviceIndex); } constructor( public Vectors: VectorSubcommand[], public DeviceIndex: number = -1, public Id: number = DEFAULT_MESSAGE_ID, ) { super(DeviceIndex, Id); } } export class SensorReadCmd extends ButtplugDeviceMessage { static Name = "SensorReadCmd"; constructor( public DeviceIndex: number, public SensorIndex: number, public SensorType: SensorType, public Id: number = DEFAULT_MESSAGE_ID, ) { super(DeviceIndex, Id); } } export class SensorReading extends ButtplugDeviceMessage { static Name = "SensorReading"; constructor( public DeviceIndex: number, public SensorIndex: number, public SensorType: SensorType, public Data: number[], public Id: number = DEFAULT_MESSAGE_ID, ) { super(DeviceIndex, Id); } } export class RawReadCmd extends ButtplugDeviceMessage { static Name = "RawReadCmd"; constructor( public DeviceIndex: number, public Endpoint: string, public ExpectedLength: number, public Timeout: number, public Id: number = DEFAULT_MESSAGE_ID, ) { super(DeviceIndex, Id); } } export class RawWriteCmd extends ButtplugDeviceMessage { static Name = "RawWriteCmd"; constructor( public DeviceIndex: number, public Endpoint: string, public Data: Uint8Array, public WriteWithResponse: boolean, public Id: number = DEFAULT_MESSAGE_ID, ) { super(DeviceIndex, Id); } } export class RawSubscribeCmd extends ButtplugDeviceMessage { static Name = "RawSubscribeCmd"; constructor( public DeviceIndex: number, public Endpoint: string, public Id: number = DEFAULT_MESSAGE_ID, ) { super(DeviceIndex, Id); } } export class RawUnsubscribeCmd extends ButtplugDeviceMessage { static Name = "RawUnsubscribeCmd"; constructor( public DeviceIndex: number, public Endpoint: string, public Id: number = DEFAULT_MESSAGE_ID, ) { super(DeviceIndex, Id); } } export class RawReading extends ButtplugDeviceMessage { static Name = "RawReading"; constructor( public DeviceIndex: number, public Endpoint: string, public Data: number[], public Id: number = DEFAULT_MESSAGE_ID, ) { super(DeviceIndex, Id); } }