Skip to content
Merged
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
46 changes: 46 additions & 0 deletions src/device/knownDeviceRegistry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import Settings from '../settings/settings.js';
import KnownDevice from '../settings/knownDevice.js';
import DeviceNameGenerator from './deviceNameGenerator.js';
import Logger from '../logging/Logger.js';
import { DeviceId } from './deviceId.js';

export default class KnownDeviceRegistry
{
private readonly settings: Settings;

private readonly nameGenerator: DeviceNameGenerator;

private readonly logger: Logger;

public constructor(settings: Settings, nameGenerator: DeviceNameGenerator, logger: Logger) {
this.settings = settings;
this.nameGenerator = nameGenerator;
this.logger = logger.child({ name: KnownDeviceRegistry.name });
}

public resolve(deviceId: DeviceId, type: string, provider: string, name?: string): KnownDevice {
const knownDevice = this.settings.getKnownDeviceById(deviceId);

if (undefined !== knownDevice && knownDevice.type === type) {
// Already known (previously detected serial number)
this.logger.debug(`Device is already known: ${knownDevice.id}`);
return knownDevice;
}

if (undefined !== knownDevice) {
this.logger.warn(
`Device id ${knownDevice.id} is known, but as type '${knownDevice.type}' instead of '${type}' - treating as an unknown device`
);
}

return new KnownDevice(deviceId, name ?? this.nameGenerator.generateName(), type, provider);
}

public persist(knownDevice: KnownDevice): void {
if (this.settings.getKnownDeviceById(knownDevice.id) === knownDevice) {
return;
}

this.settings.addKnownDevice(knownDevice);
}
}
27 changes: 8 additions & 19 deletions src/device/protocol/airotic/airoticDeviceProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import AiroticProtocol from './airtonicProtocol.js';
import MessageResponseHandler from '../messageResponseHandler.js';
import StrDeviceAttribute from '../../attribute/strDeviceAttribute.js';
import { DeviceAttributeModifier } from '../../attribute/deviceAttribute.js';
import Settings from '../../../settings/settings.js';
import KnownDevice from '../../../settings/knownDevice.js';
import { DeviceId } from '../../deviceId.js';
import KnownDeviceRegistry from '../../knownDeviceRegistry.js';
import BoolDeviceAttribute from '../../attribute/boolDeviceAttribute.js';
import FloatDeviceAttribute from '../../attribute/floatDeviceAttribute.js';
import BleDeviceProvider from '../../provider/bleDeviceProvider.js';
Expand All @@ -25,12 +23,12 @@ export default class AiroticDeviceProvider extends BleDeviceProvider<AiroticDevi
private static readonly UART_RX_CHAR_UUID = '6e400002b5a3f393e0a9e50e24dcca9e';
private static readonly UART_TX_CHAR_UUID = '6e400003b5a3f393e0a9e50e24dcca9e';

private readonly settings: Settings;
private readonly knownDeviceRegistry: KnownDeviceRegistry;

public constructor(deviceManager: DeviceManager, settings: Settings, eventEmitter: EventEmitter, logger: Logger) {
public constructor(deviceManager: DeviceManager, knownDeviceRegistry: KnownDeviceRegistry, eventEmitter: EventEmitter, logger: Logger) {
super(deviceManager, eventEmitter, logger.child({ name: AiroticDeviceProvider.name }));

this.settings = settings;
this.knownDeviceRegistry = knownDeviceRegistry;
}

public override async init(): Promise<void> {
Expand All @@ -56,8 +54,10 @@ export default class AiroticDeviceProvider extends BleDeviceProvider<AiroticDevi
return undefined;
}

const knownDevice = this.createKnownDevice(
const knownDevice = this.knownDeviceRegistry.resolve(
deviceInfo.id,
'airotic',
AiroticDeviceProvider.providerName,
deviceInfo.peripheral.advertisement.localName ?? `Airotic ${deviceInfo.id}`,
);

Expand Down Expand Up @@ -85,7 +85,7 @@ export default class AiroticDeviceProvider extends BleDeviceProvider<AiroticDevi
this.logger,
);

this.settings.addKnownDevice(knownDevice);
this.knownDeviceRegistry.persist(knownDevice);

return device;
}
Expand Down Expand Up @@ -126,15 +126,4 @@ export default class AiroticDeviceProvider extends BleDeviceProvider<AiroticDevi

return false;
}

private createKnownDevice(deviceId: DeviceId, deviceName: string): KnownDevice {
const knownDevice = this.settings.getKnownDeviceById(deviceId);

if (undefined !== knownDevice) {
this.logger.debug(`Device is already known: ${knownDevice.id}`);
return knownDevice;
}

return new KnownDevice(deviceId, deviceName, 'airotic', AiroticDeviceProvider.providerName);
}
}
28 changes: 10 additions & 18 deletions src/device/protocol/buttplugIo/buttplugIoDeviceFactory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Settings from '../../../settings/settings.js';
import { ButtplugClientDevice } from 'buttplug';
import ButtplugIoDevice, { ButtplugIoDeviceAttributeKey, ButtplugIoDeviceAttributes } from './buttplugIoDevice.js';
import KnownDeviceRegistry from '../../knownDeviceRegistry.js';
import KnownDevice from '../../../settings/knownDevice.js';
import Logger from '../../../logging/Logger.js';
import { DeviceAttributeModifier } from '../../attribute/deviceAttribute.js';
Expand All @@ -17,22 +17,22 @@ export default class ButtplugIoDeviceFactory
{
private readonly dateFactory: DateFactory;

private readonly settings: Settings;
private readonly knownDeviceRegistry: KnownDeviceRegistry;

private readonly logger: Logger;

private readonly eventEmitterFactory: EventEmitterFactory;

public constructor(dateFactory: DateFactory, eventEmitterFactory: EventEmitterFactory, settings: Settings, logger: Logger) {
public constructor(dateFactory: DateFactory, eventEmitterFactory: EventEmitterFactory, knownDeviceRegistry: KnownDeviceRegistry, logger: Logger) {
this.dateFactory = dateFactory;
this.eventEmitterFactory = eventEmitterFactory;

this.settings = settings;
this.knownDeviceRegistry = knownDeviceRegistry;
this.logger = logger;
}

public create(buttplugDevice: ButtplugClientDevice, provider: string, useDeviceNameAsId: boolean): ButtplugIoDevice {
const knownDevice = this.createKnownDevice(buttplugDevice, provider, useDeviceNameAsId);
const knownDevice = this.resolveKnownDevice(buttplugDevice, provider, useDeviceNameAsId);

const deviceAttrs = ButtplugIoDeviceFactory.parseDeviceAttributes(buttplugDevice);

Expand All @@ -51,7 +51,7 @@ export default class ButtplugIoDeviceFactory
throw new Error('Unknown device type: ' + knownDevice.name);
}

this.settings.addKnownDevice(knownDevice);
this.knownDeviceRegistry.persist(knownDevice);

return device;
}
Expand Down Expand Up @@ -110,26 +110,18 @@ export default class ButtplugIoDeviceFactory
return attributes;
}

private createKnownDevice(buttplugDevice: ButtplugClientDevice, provider: string, useDeviceNameAsId: boolean): KnownDevice {
private resolveKnownDevice(buttplugDevice: ButtplugClientDevice, provider: string, useDeviceNameAsId: boolean): KnownDevice {
// Since we don't get a unique identifier for the Bluetooth device from Intiface,
// we need to use the index assigned to the device by Intiface. It's the best we have.
// or the name if using Intiface-engine without id persistence
const nameString = buttplugDevice.name.replace(/[^a-zA-Z0-9]/g, '');
const deviceId = DeviceId.create(useDeviceNameAsId ? `buttplugio-${nameString}` : `buttplugio-${buttplugDevice.index}`);

const knownDevice = this.settings.getKnownDeviceById(deviceId)

if (undefined !== knownDevice) {
// Return already existing device if already known (previously detected serial number)
this.logger.debug(`Device is already known: ${knownDevice.id}`);
return knownDevice;
}

return new KnownDevice(
return this.knownDeviceRegistry.resolve(
deviceId,
buttplugDevice.displayName ?? buttplugDevice.name,
buttplugDevice.name,
provider
provider,
buttplugDevice.displayName ?? buttplugDevice.name,
);
}
}
21 changes: 10 additions & 11 deletions src/device/protocol/estim2b/estim2bDeviceFactory.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Settings from '../../../settings/settings.js';
import DeviceNameGenerator from '../../deviceNameGenerator.js';
import KnownDeviceRegistry from '../../knownDeviceRegistry.js';
import DateFactory from '../../../factory/dateFactory.js';
import Logger from '../../../logging/Logger.js';
import { DeviceAttributeModifier } from '../../attribute/deviceAttribute.js';
Expand All @@ -19,9 +18,7 @@ export default class Estim2bDeviceFactory
{
private readonly dateFactory: DateFactory;

private readonly settings: Settings;

private readonly nameGenerator: DeviceNameGenerator;
private readonly knownDeviceRegistry: KnownDeviceRegistry;

private readonly logger: Logger;

Expand All @@ -30,15 +27,13 @@ export default class Estim2bDeviceFactory
public constructor(
dateFactory: DateFactory,
eventEmitterFactory: EventEmitterFactory,
settings: Settings,
nameGenerator: DeviceNameGenerator,
knownDeviceRegistry: KnownDeviceRegistry,
logger: Logger
) {
this.dateFactory = dateFactory;
this.eventEmitterFactory = eventEmitterFactory;

this.settings = settings;
this.nameGenerator = nameGenerator;
this.knownDeviceRegistry = knownDeviceRegistry;
this.logger = logger;
}

Expand All @@ -50,10 +45,14 @@ export default class Estim2bDeviceFactory
provider: string
): Promise<Estim2bDevice> {
const attributes = this.getAttributes(initialStatus);
const knownDevice = this.knownDeviceRegistry.resolve(deviceId, 'estim2b', provider);

// KnownDevice is not persisted as we cannot determine a unique device id for the estim2b device,
// so we cannot reliably identify it on future connections.

return new Estim2bDevice(
deviceId,
this.nameGenerator.generateName(),
knownDevice.id,
knownDevice.name,
provider,
this.dateFactory.now(),
true,
Expand Down
36 changes: 6 additions & 30 deletions src/device/protocol/slvCtrlPlus/slvCtrlPlusDeviceFactory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import Settings from '../../../settings/settings.js';
import KnownDevice from '../../../settings/knownDevice.js';
import DeviceNameGenerator from '../../deviceNameGenerator.js';
import KnownDeviceRegistry from '../../knownDeviceRegistry.js';
import GenericSlvCtrlPlusDevice from './genericSlvCtrlPlusDevice.js';
import DateFactory from '../../../factory/dateFactory.js';
import DeviceBidirectionalTransport from '../../transport/deviceBidirectionalTransport.js';
Expand All @@ -19,30 +17,26 @@ export default class SlvCtrlPlusDeviceFactory

protected readonly eventEmitterFactory: EventEmitterFactory;

private readonly settings: Settings;

private readonly nameGenerator: DeviceNameGenerator;
private readonly knownDeviceRegistry: KnownDeviceRegistry;

private readonly logger: Logger;

public constructor(
dateFactory: DateFactory,
eventEmitterFactory: EventEmitterFactory,
settings: Settings,
nameGenerator: DeviceNameGenerator,
knownDeviceRegistry: KnownDeviceRegistry,
logger: Logger
) {
this.dateFactory = dateFactory;
this.eventEmitterFactory = eventEmitterFactory;
this.settings = settings;
this.nameGenerator = nameGenerator;
this.knownDeviceRegistry = knownDeviceRegistry;
this.logger = logger.child({ name: SlvCtrlPlusDeviceFactory.name });
}

public async create(deviceId: DeviceId, transport: DeviceBidirectionalTransport, provider: string): Promise<GenericSlvCtrlPlusDevice> {
const deviceInfo = await this.getDeviceInfo(transport);
const protocol = deviceInfo.protocol;
const knownDevice = this.createKnownDevice(deviceId, deviceInfo.deviceType, provider);
const knownDevice = this.knownDeviceRegistry.resolve(deviceId, deviceInfo.deviceType, provider);
const deviceAttributes = await this.getAttributes(transport, protocol);

const device = new GenericSlvCtrlPlusDevice(
Expand All @@ -60,7 +54,7 @@ export default class SlvCtrlPlusDeviceFactory
this.logger,
);

this.settings.addKnownDevice(knownDevice);
this.knownDeviceRegistry.persist(knownDevice);

return device;
}
Expand Down Expand Up @@ -125,22 +119,4 @@ export default class SlvCtrlPlusDeviceFactory

return new SlvCtrlProtocolV1();
}

private createKnownDevice(deviceId: DeviceId, deviceType: string, provider: string): KnownDevice {
const knownDevice = this.settings.getKnownDeviceById(deviceId)

if (undefined !== knownDevice) {
// Return already existing device if already known (previously detected serial number)
this.logger.debug(`Device is already known: ${knownDevice.id}`);
return knownDevice;
}

// Create a new device and return if not yet known (new serial number)
return new KnownDevice(
deviceId,
this.nameGenerator.generateName(),
deviceType,
provider
);
}
}
37 changes: 7 additions & 30 deletions src/device/protocol/zc95/zc95DeviceFactory.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Settings from '../../../settings/settings.js';
import DeviceNameGenerator from '../../deviceNameGenerator.js';
import KnownDeviceRegistry from '../../knownDeviceRegistry.js';
import DateFactory from '../../../factory/dateFactory.js';
import Logger from '../../../logging/Logger.js';
import Zc95Device, { Zc95DeviceAttributes } from './zc95Device.js';
Expand All @@ -14,31 +13,26 @@ import MessageResponseHandler from '../messageResponseHandler.js';
import EventEmitterFactory from '../../../factory/eventEmitterFactory.js';
import { logError } from '../../../util/error.js';
import { DeviceId } from '../../deviceId.js';
import KnownDevice from '../../../settings/knownDevice.js';

export default class Zc95DeviceFactory
{
private readonly dateFactory: DateFactory;

private readonly eventEmitterFactory: EventEmitterFactory;

private readonly settings: Settings;

private readonly nameGenerator: DeviceNameGenerator;
private readonly knownDeviceRegistry: KnownDeviceRegistry;

private readonly logger: Logger;

public constructor(
dateFactory: DateFactory,
eventEmitterFactory: EventEmitterFactory,
settings: Settings,
nameGenerator: DeviceNameGenerator,
knownDeviceRegistry: KnownDeviceRegistry,
logger: Logger
) {
this.dateFactory = dateFactory;
this.eventEmitterFactory = eventEmitterFactory;
this.settings = settings;
this.nameGenerator = nameGenerator;
this.knownDeviceRegistry = knownDeviceRegistry;
this.logger = logger;
}

Expand All @@ -62,8 +56,9 @@ export default class Zc95DeviceFactory
);

// We only receive serial no. info for ZC95 devices with fw >=2.0
const knownDevice = this.createKnownDevice(
const knownDevice = this.knownDeviceRegistry.resolve(
versionDetails.SerialNo !== undefined ? DeviceId.create(versionDetails.SerialNo) : deviceId,
'zc95',
provider,
);

Expand All @@ -86,7 +81,7 @@ export default class Zc95DeviceFactory

// Only store the known device if we have a deterministic device id based on serial no. info of the zc95 fw
if (versionDetails.SerialNo !== undefined) {
this.settings.addKnownDevice(knownDevice);
this.knownDeviceRegistry.persist(knownDevice);
}

return device;
Expand All @@ -110,22 +105,4 @@ export default class Zc95DeviceFactory
patternStarted: patternStartedAttr,
};
}

private createKnownDevice(deviceId: DeviceId, provider: string): KnownDevice {
const knownDevice = this.settings.getKnownDeviceById(deviceId)

if (undefined !== knownDevice) {
// Return already existing device if already known (previously detected serial number)
this.logger.debug(`Device is already known: ${knownDevice.id}`);
return knownDevice;
}

// Create a new device and return if not yet known (new serial number)
return new KnownDevice(
deviceId,
this.nameGenerator.generateName(),
'zc95',
provider
);
}
}
Loading
Loading