Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 45 additions & 2 deletions src/device/device.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import {Exclude, Expose} from "class-transformer";
import DeviceState from "./deviceState.js";
import DeviceProviderEvent from "./provider/deviceProviderEvent.js";
import EventEmitter from "events";
import DeviceEvent from "./deviceEvent.js";

@Exclude()
export default abstract class Device
Expand Down Expand Up @@ -28,24 +31,57 @@ export default abstract class Device
@Expose()
protected lastRefresh: Date;

private readonly eventEmitter: EventEmitter;

private refreshTimer: NodeJS.Timeout;

protected constructor(
deviceId: string,
deviceName: string,
provider: string,
connectedSince: Date,
controllable: boolean
controllable: boolean,
eventEmitter: EventEmitter
) {
this.deviceId = deviceId;
this.deviceName = deviceName;
this.provider = provider;
this.connectedSince = connectedSince;
this.controllable = controllable;
this.eventEmitter = eventEmitter;
this.state = DeviceState.ready;
}

public abstract refreshData(): Promise<void>;

public updateLastRefresh(): void
public start(): void
{
const deviceStatusUpdater = () => {
if (this.getState === DeviceState.busy) {
return;
}

this.refreshData().then(() => this.updateLastRefresh()).catch(
(e: Error) => this.eventEmitter.emit(DeviceEvent.deviceError, this, e)
);

this.eventEmitter.emit(DeviceEvent.deviceRefreshed, this);
};

this.refreshTimer = setInterval(deviceStatusUpdater, this.getRefreshInterval);

setTimeout(deviceStatusUpdater, 0); // Immediately execute async
}

public stop(): void
{
if (undefined !== this.refreshTimer) {
clearInterval(this.refreshTimer);
this.refreshTimer = undefined;
}
}

protected updateLastRefresh(): void
{
this.lastRefresh = new Date();
}
Expand All @@ -72,4 +108,11 @@ export default abstract class Device
public get getState(): DeviceState {
return this.state;
}

public on(event: DeviceEvent, listener: ((device: Device) => void)|((device: Device, e: Error) => void)): this
{
this.eventEmitter.on(event, listener);

return this;
}
}
8 changes: 8 additions & 0 deletions src/device/deviceEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
enum DeviceEvent {
deviceConnected = "deviceConnected",
deviceDisconnected = "deviceDisconnected",
deviceRefreshed = "deviceRefreshed",
deviceError = "deviceError",
}

export default DeviceEvent;
29 changes: 23 additions & 6 deletions src/device/deviceManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,43 @@ import EventEmitter from "events";
import DeviceProvider from "./provider/deviceProvider.js";
import DeviceManagerEvent from "./deviceManagerEvent.js";
import DeviceProviderEvent from "./provider/deviceProviderEvent.js";
import DeviceEvent from "./deviceEvent.js";
import Logger from "../logging/Logger.js";

export default class DeviceManager
{
private eventEmitter: EventEmitter;
private readonly eventEmitter: EventEmitter;

private connectedDevices: Map<string, Device>;

private deviceProviders: DeviceProvider[] = [];

public constructor(eventEmitter: EventEmitter, connectedDevices: Map<string, Device>) {
private readonly logger: Logger;

public constructor(eventEmitter: EventEmitter, connectedDevices: Map<string, Device>, logger: Logger) {
this.eventEmitter = eventEmitter;
this.connectedDevices = connectedDevices;
this.logger = logger;
}

public registerDeviceProvider(deviceProvider: DeviceProvider): void
public async registerDeviceProvider(deviceProvider: DeviceProvider): Promise<void>
{
deviceProvider.on(DeviceProviderEvent.deviceConnected, (device: Device) => this.addDevice(device));
deviceProvider.on(DeviceProviderEvent.deviceDisconnected, (device: Device) => this.removeDevice(device));
deviceProvider.on(DeviceProviderEvent.deviceRefreshed, (device: Device) => this.refreshDevice(device));

this.deviceProviders.push(deviceProvider);

void deviceProvider.init(this);
await deviceProvider.init(this);
}

public addDevice(device: Device): void
{
device.on(DeviceEvent.deviceError, (d: Device, e: Error) => {
this.logger.error(`Error for device ${d.getDeviceId}: ${e.message}`, e);
});

device.on(DeviceEvent.deviceRefreshed, (d: Device) => this.refreshDevice(d))

this.connectedDevices.set(device.getDeviceId, device);
this.eventEmitter.emit(DeviceManagerEvent.deviceConnected, device);
}
Expand All @@ -40,7 +50,7 @@ export default class DeviceManager
this.eventEmitter.emit(DeviceManagerEvent.deviceDisconnected, device);
}

public refreshDevice(device: Device)
private refreshDevice(device: Device)
{
this.eventEmitter.emit(DeviceManagerEvent.deviceRefreshed, device);
}
Expand All @@ -61,4 +71,11 @@ export default class DeviceManager
{
this.eventEmitter.on(event, listener);
}

public async close(): Promise<void>
{
for (const p of this.deviceProviders) {
await p.close();
}
}
}
8 changes: 4 additions & 4 deletions src/device/protocol/buttplugIo/buttplugIoDevice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import GenericDeviceAttributeDiscriminator
from "../../../serialization/discriminator/genericDeviceAttributeDiscriminator.js";
import RangeGenericDeviceAttribute from "../../attribute/rangeGenericDeviceAttribute.js";
import BoolGenericDeviceAttribute from "../../attribute/boolGenericDeviceAttribute.js";
import EventEmitter from "events";

@Exclude()
export default class ButtplugIoDevice extends Device
Expand All @@ -29,9 +30,10 @@ export default class ButtplugIoDevice extends Device
provider: string,
connectedSince: Date,
buttplugClientDevice: ButtplugClientDevice,
attributes: GenericDeviceAttribute[]
attributes: GenericDeviceAttribute[],
eventEmitter: EventEmitter,
) {
super(deviceId, deviceName, provider, connectedSince, true);
super(deviceId, deviceName, provider, connectedSince, true, eventEmitter);
this.buttplugClientDevice = buttplugClientDevice;
this.attributes = attributes;
this.deviceModel = deviceModel;
Expand All @@ -43,8 +45,6 @@ export default class ButtplugIoDevice extends Device
const value = await this.buttplugClientDevice.sensorRead(sensor.Index, sensor.SensorType);
this.data[`${sensor.SensorType}-${sensor.Index}`] = value[0];
}

this.updateLastRefresh();
}

private initData(attributes: GenericDeviceAttribute[]): void
Expand Down
7 changes: 6 additions & 1 deletion src/device/protocol/buttplugIo/buttplugIoDeviceFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import FloatGenericDeviceAttribute from "../../attribute/floatGenericDeviceAttri
import RangeGenericDeviceAttribute from "../../attribute/rangeGenericDeviceAttribute.js";
import BoolGenericDeviceAttribute from "../../attribute/boolGenericDeviceAttribute.js";
import DateFactory from "../../../factory/dateFactory.js";
import EventEmitter from "events";


export default class ButtplugIoDeviceFactory
Expand All @@ -21,11 +22,14 @@ export default class ButtplugIoDeviceFactory

private readonly logger: Logger;

public constructor(uuidFactory: UuidFactory, dateFactory: DateFactory, settings: Settings, logger: Logger) {
private readonly eventEmitter: EventEmitter;

public constructor(uuidFactory: UuidFactory, dateFactory: DateFactory, settings: Settings, logger: Logger, eventEmitter: EventEmitter) {
this.uuidFactory = uuidFactory;
this.dateFactory = dateFactory;
this.settings = settings;
this.logger = logger;
this.eventEmitter = eventEmitter;
}

public create(buttplugDevice: ButtplugClientDevice, provider: string, useDeviceNameAsId: boolean): ButtplugIoDevice {
Expand All @@ -41,6 +45,7 @@ export default class ButtplugIoDeviceFactory
this.dateFactory.now(),
buttplugDevice,
deviceAttrs,
this.eventEmitter,
);

if (null === device) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export default class ButtplugIoWebsocketDeviceProvider extends DeviceProvider
public static readonly name = 'buttplugIoWebsocket';

private connectedDevices: Map<number, ButtplugIoDevice> = new Map();
private deviceUpdaters: Map<number, NodeJS.Timeout> = new Map();

private buttplugConnector: ButtplugNodeWebsocketClientConnector;
private buttplugClient: ButtplugClient;
Expand Down Expand Up @@ -66,6 +65,12 @@ export default class ButtplugIoWebsocketDeviceProvider extends DeviceProvider
});
}

public async close(): Promise<void> {
this.connectedDevices.forEach(d => d.stop());

await this.buttplugClient.disconnect();
}

private connectToServer(): void {
if (this.buttplugClient.connected) {
return;
Expand Down Expand Up @@ -101,10 +106,10 @@ export default class ButtplugIoWebsocketDeviceProvider extends DeviceProvider

try {
const device = this.buttplugIoDeviceFactory.create(buttplugDevice, ButtplugIoWebsocketDeviceProvider.name, this.useDeviceNameAsId);
const deviceStatusUpdaterInterval = this.initDeviceStatusUpdater(device);

device.start();

this.connectedDevices.set(buttplugDevice.index, device);
this.deviceUpdaters.set(buttplugDevice.index, deviceStatusUpdaterInterval);

this.eventEmitter.emit(DeviceProviderEvent.deviceConnected, device);

Expand All @@ -117,9 +122,9 @@ export default class ButtplugIoWebsocketDeviceProvider extends DeviceProvider

private removeButtplugIoDevice(buttplugDevice: ButtplugClientDevice): void {
const device = this.connectedDevices.get(buttplugDevice.index);
const deviceUpdaterInterval = this.deviceUpdaters.get(buttplugDevice.index);

clearInterval(deviceUpdaterInterval);
device.stop();

this.eventEmitter.emit(DeviceProviderEvent.deviceDisconnected, device);

this.connectedDevices.delete(buttplugDevice.index);
Expand Down
8 changes: 4 additions & 4 deletions src/device/protocol/slvCtrlPlus/genericSlvCtrlPlusDevice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import GenericDeviceAttributeDiscriminator from "../../../serialization/discrimi
import DeviceState from "../../deviceState.js";
import DeviceTransport from "../../transport/deviceTransport.js";
import SlvCtrlPlusMessageParser from "./slvCtrlPlusMessageParser.js";
import EventEmitter from "events";

@Exclude()
export default class GenericSlvCtrlPlusDevice extends SlvCtrlPlusDevice
Expand Down Expand Up @@ -37,9 +38,10 @@ export default class GenericSlvCtrlPlusDevice extends SlvCtrlPlusDevice
connectedSince: Date,
transport: DeviceTransport,
protocolVersion: number,
attributes: GenericDeviceAttribute[]
attributes: GenericDeviceAttribute[],
eventEmitter: EventEmitter,
) {
super(deviceId, deviceName, provider, connectedSince, transport, false);
super(deviceId, deviceName, provider, connectedSince, transport, false, eventEmitter);

this.deviceModel = deviceModel;
this.fwVersion = fwVersion;
Expand All @@ -64,8 +66,6 @@ export default class GenericSlvCtrlPlusDevice extends SlvCtrlPlusDevice

this.data[attrKey] = ('' !== dataObj[attrKey]) ? attrDef.fromString(dataObj[attrKey]) : null;
}

this.updateLastRefresh();
}

public async setAttribute(attributeName: string, value: string|number|boolean|null): Promise<string> {
Expand Down
6 changes: 4 additions & 2 deletions src/device/protocol/slvCtrlPlus/slvCtrlPlusDevice.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {Exclude} from "class-transformer";
import Device from "../../device.js";
import DeviceTransport from "../../transport/deviceTransport.js";
import EventEmitter from "events";

@Exclude()
export default abstract class SlvCtrlPlusDevice extends Device
Expand All @@ -13,9 +14,10 @@ export default abstract class SlvCtrlPlusDevice extends Device
provider: string,
connectedSince: Date,
transport: DeviceTransport,
controllable: boolean
controllable: boolean,
eventEmitter: EventEmitter,
) {
super(deviceId, deviceName, provider, connectedSince, controllable);
super(deviceId, deviceName, provider, connectedSince, controllable, eventEmitter);
this.transport = transport;
}

Expand Down
10 changes: 8 additions & 2 deletions src/device/protocol/slvCtrlPlus/slvCtrlPlusDeviceFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import DateFactory from "../../../factory/dateFactory.js";
import DeviceTransport from "../../transport/deviceTransport.js";
import SlvCtrlPlusMessageParser from "./slvCtrlPlusMessageParser.js";
import Logger from "../../../logging/Logger.js";
import EventEmitter from "events";

export default class SlvCtrlPlusDeviceFactory
{
Expand All @@ -21,18 +22,22 @@ export default class SlvCtrlPlusDeviceFactory

private readonly logger: Logger;

private readonly eventEmitter: EventEmitter;

public constructor(
uuidFactory: UuidFactory,
dateFactory: DateFactory,
settings: Settings,
nameGenerator: DeviceNameGenerator,
logger: Logger
logger: Logger,
eventEmitter: EventEmitter,
) {
this.uuidFactory = uuidFactory;
this.dateFactory = dateFactory;
this.settings = settings;
this.nameGenerator = nameGenerator;
this.logger = logger;
this.eventEmitter = eventEmitter;
}

public async create(deviceInfoStr: string, transport: DeviceTransport, provider: string): Promise<Device> {
Expand All @@ -52,7 +57,8 @@ export default class SlvCtrlPlusDeviceFactory
this.dateFactory.now(),
transport,
Number(protocolVersion),
deviceAttrs
deviceAttrs,
this.eventEmitter,
);

this.settings.addKnownDevice(knownDevice);
Expand Down
Loading