incyclist-devices 3.0.24 → 3.0.25

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.
@@ -55,7 +55,7 @@ class BleAdapter extends adpater_js_1.default {
55
55
  const info = peripheral.getInfo();
56
56
  const settings = { ...this.settings };
57
57
  settings.id = settings.id ?? info.id;
58
- settings.address = settings.address ?? info.address;
58
+ settings.address = info.address ?? settings.address;
59
59
  settings.name = settings.name ?? info.name;
60
60
  this.settings = settings;
61
61
  }
@@ -77,11 +77,13 @@ class BleAdapter extends adpater_js_1.default {
77
77
  if (as.profile || settings.profile) {
78
78
  return (as.protocol === settings.protocol && as.profile === settings.profile && as.name === settings.name);
79
79
  }
80
- else {
81
- return (as.protocol === settings.protocol && ((as.name && settings.name && as.name === settings.name) ||
82
- (as.address && settings.address && as.address === settings.address) ||
83
- (as.id && settings.id && as.id === settings.id)));
84
- }
80
+ if (as.protocol !== settings.protocol)
81
+ return false;
82
+ if (as.id && settings.id)
83
+ return as.id === settings.id;
84
+ if (as.address && settings.address)
85
+ return as.address === settings.address;
86
+ return !!(as.name && settings.name && as.name === settings.name);
85
87
  }
86
88
  isSame(adapter) {
87
89
  return this.isEqual(adapter.getSettings());
@@ -72,14 +72,18 @@ class DirectConnectInterface extends node_events_1.EventEmitter {
72
72
  const protocol = this.getProtocol(service);
73
73
  const address = service.address;
74
74
  const services = service.serviceUUIDs?.map(uuid => (0, index_js_1.beautifyUUID)(uuid, false))?.join(',');
75
- return { interface: DirectConnectInterface.INTERFACE_NAME, name, protocol, address, services };
75
+ const id = service.serialNo || undefined;
76
+ return { interface: DirectConnectInterface.INTERFACE_NAME, name, protocol, address, services, ...(id ? { id } : {}) };
76
77
  }
77
78
  catch {
78
79
  return null;
79
80
  }
80
81
  }
81
82
  createPeripheralFromSettings(settings) {
82
- const info = this.getAll().find(a => a.service.name === settings.name);
83
+ const bleSettings = settings;
84
+ const all = this.getAll();
85
+ const info = (bleSettings.address && all.find(a => a.service.name === settings.name && a.service.address === bleSettings.address))
86
+ ?? all.find(a => a.service.name === settings.name);
83
87
  if (!info?.service)
84
88
  return null;
85
89
  return this.createPeripheral(info.service);
@@ -247,35 +251,68 @@ class DirectConnectInterface extends node_events_1.EventEmitter {
247
251
  const src = source === 'known-device' ? 'known-device' : 'mdns';
248
252
  try {
249
253
  service.transport = this.getName();
250
- const existing = this.find(service);
251
- if (existing) {
252
- const idx = this.services.indexOf(existing);
253
- this.services[idx] = { ts: Date.now(), service };
254
- if (src !== 'known-device' && existing.source === 'known-device') {
255
- this.services[idx].source = src;
256
- this.logEvent({ message: 'device re-announced', device: service.name, announcement: service, source });
257
- this.emitDevice(service);
258
- }
254
+ const sameAddress = this.findByNameAndAddress(service.name, service.address);
255
+ if (sameAddress) {
256
+ this.refreshSameAddressEntry(sameAddress, service, src, source);
257
+ return;
258
+ }
259
+ const sameName = this.findAllByName(service.name);
260
+ if (src === 'known-device' && sameName.some(a => a.source !== 'known-device')) {
261
+ return;
262
+ }
263
+ if (sameName.length === 0) {
264
+ this.announceNewEntry(service, src, source, 'device announced');
265
+ return;
259
266
  }
260
- else {
261
- this.services.push({ ts: Date.now(), service, source: src });
262
- if (!service.serviceUUIDs?.length)
263
- return;
264
- this.logEvent({ message: 'device announced', device: service.name, announcement: service, source });
265
- this.emitDevice(service);
266
- this.matching?.push(service.name);
267
+ const realEntries = sameName.filter(a => a.source !== 'known-device');
268
+ if (src !== 'known-device' && realEntries.length >= 1) {
269
+ this.announceNewEntry(service, src, source, 'device announced (name collision, distinct address)');
270
+ return;
267
271
  }
272
+ this.reannounceExistingEntry(sameName[0], service, src, source);
268
273
  }
269
274
  catch (err) {
270
275
  this.logError(err, 'addService');
271
276
  }
272
277
  }
273
- find(service) {
274
- return this.services.find(a => a.service.name === service.name && a.ts > Date.now() - DC_EXPIRATION_TIMEOUT);
278
+ refreshSameAddressEntry(sameAddress, service, src, source) {
279
+ const idx = this.services.indexOf(sameAddress);
280
+ const wasKnownDevicePlaceholder = sameAddress.source === 'known-device';
281
+ const nextSource = (wasKnownDevicePlaceholder && src !== 'known-device') ? src : sameAddress.source;
282
+ this.services[idx] = { ts: Date.now(), service, source: nextSource };
283
+ if (src !== 'known-device' && wasKnownDevicePlaceholder) {
284
+ this.logEvent({ message: 'device re-announced', device: service.name, announcement: service, source });
285
+ this.emitDevice(service);
286
+ }
287
+ }
288
+ announceNewEntry(service, src, source, message) {
289
+ this.services.push({ ts: Date.now(), service, source: src });
290
+ if (!service.serviceUUIDs?.length)
291
+ return;
292
+ this.logEvent({ message, device: service.name, announcement: service, source });
293
+ this.emitDevice(service);
294
+ this.matching?.push(service.name);
295
+ }
296
+ reannounceExistingEntry(existing, service, src, source) {
297
+ const idx = this.services.indexOf(existing);
298
+ this.services[idx] = { ts: Date.now(), service, source: src };
299
+ if (src !== 'known-device') {
300
+ this.logEvent({ message: 'device re-announced', device: service.name, announcement: service, source });
301
+ this.emitDevice(service);
302
+ }
303
+ }
304
+ findByNameAndAddress(name, address) {
305
+ return this.services.find(a => a.service.name === name && a.service.address === address && a.ts > Date.now() - DC_EXPIRATION_TIMEOUT);
306
+ }
307
+ findAllByName(name) {
308
+ return this.services.filter(a => a.service.name === name && a.ts > Date.now() - DC_EXPIRATION_TIMEOUT);
275
309
  }
276
310
  getAll() {
277
311
  return this.services.filter(a => a.ts > Date.now() - DC_EXPIRATION_TIMEOUT);
278
312
  }
313
+ hasNameCollision(name) {
314
+ return this.findAllByName(name).length > 1;
315
+ }
279
316
  setDebug(enabled) {
280
317
  this.debug = enabled;
281
318
  }
@@ -50,7 +50,7 @@ export default class BleAdapter extends IncyclistDevice {
50
50
  const info = peripheral.getInfo();
51
51
  const settings = { ...this.settings };
52
52
  settings.id = settings.id ?? info.id;
53
- settings.address = settings.address ?? info.address;
53
+ settings.address = info.address ?? settings.address;
54
54
  settings.name = settings.name ?? info.name;
55
55
  this.settings = settings;
56
56
  }
@@ -72,11 +72,13 @@ export default class BleAdapter extends IncyclistDevice {
72
72
  if (as.profile || settings.profile) {
73
73
  return (as.protocol === settings.protocol && as.profile === settings.profile && as.name === settings.name);
74
74
  }
75
- else {
76
- return (as.protocol === settings.protocol && ((as.name && settings.name && as.name === settings.name) ||
77
- (as.address && settings.address && as.address === settings.address) ||
78
- (as.id && settings.id && as.id === settings.id)));
79
- }
75
+ if (as.protocol !== settings.protocol)
76
+ return false;
77
+ if (as.id && settings.id)
78
+ return as.id === settings.id;
79
+ if (as.address && settings.address)
80
+ return as.address === settings.address;
81
+ return !!(as.name && settings.name && as.name === settings.name);
80
82
  }
81
83
  isSame(adapter) {
82
84
  return this.isEqual(adapter.getSettings());
@@ -69,14 +69,18 @@ export default class DirectConnectInterface extends EventEmitter {
69
69
  const protocol = this.getProtocol(service);
70
70
  const address = service.address;
71
71
  const services = service.serviceUUIDs?.map(uuid => beautifyUUID(uuid, false))?.join(',');
72
- return { interface: DirectConnectInterface.INTERFACE_NAME, name, protocol, address, services };
72
+ const id = service.serialNo || undefined;
73
+ return { interface: DirectConnectInterface.INTERFACE_NAME, name, protocol, address, services, ...(id ? { id } : {}) };
73
74
  }
74
75
  catch {
75
76
  return null;
76
77
  }
77
78
  }
78
79
  createPeripheralFromSettings(settings) {
79
- const info = this.getAll().find(a => a.service.name === settings.name);
80
+ const bleSettings = settings;
81
+ const all = this.getAll();
82
+ const info = (bleSettings.address && all.find(a => a.service.name === settings.name && a.service.address === bleSettings.address))
83
+ ?? all.find(a => a.service.name === settings.name);
80
84
  if (!info?.service)
81
85
  return null;
82
86
  return this.createPeripheral(info.service);
@@ -244,35 +248,68 @@ export default class DirectConnectInterface extends EventEmitter {
244
248
  const src = source === 'known-device' ? 'known-device' : 'mdns';
245
249
  try {
246
250
  service.transport = this.getName();
247
- const existing = this.find(service);
248
- if (existing) {
249
- const idx = this.services.indexOf(existing);
250
- this.services[idx] = { ts: Date.now(), service };
251
- if (src !== 'known-device' && existing.source === 'known-device') {
252
- this.services[idx].source = src;
253
- this.logEvent({ message: 'device re-announced', device: service.name, announcement: service, source });
254
- this.emitDevice(service);
255
- }
251
+ const sameAddress = this.findByNameAndAddress(service.name, service.address);
252
+ if (sameAddress) {
253
+ this.refreshSameAddressEntry(sameAddress, service, src, source);
254
+ return;
255
+ }
256
+ const sameName = this.findAllByName(service.name);
257
+ if (src === 'known-device' && sameName.some(a => a.source !== 'known-device')) {
258
+ return;
259
+ }
260
+ if (sameName.length === 0) {
261
+ this.announceNewEntry(service, src, source, 'device announced');
262
+ return;
256
263
  }
257
- else {
258
- this.services.push({ ts: Date.now(), service, source: src });
259
- if (!service.serviceUUIDs?.length)
260
- return;
261
- this.logEvent({ message: 'device announced', device: service.name, announcement: service, source });
262
- this.emitDevice(service);
263
- this.matching?.push(service.name);
264
+ const realEntries = sameName.filter(a => a.source !== 'known-device');
265
+ if (src !== 'known-device' && realEntries.length >= 1) {
266
+ this.announceNewEntry(service, src, source, 'device announced (name collision, distinct address)');
267
+ return;
264
268
  }
269
+ this.reannounceExistingEntry(sameName[0], service, src, source);
265
270
  }
266
271
  catch (err) {
267
272
  this.logError(err, 'addService');
268
273
  }
269
274
  }
270
- find(service) {
271
- return this.services.find(a => a.service.name === service.name && a.ts > Date.now() - DC_EXPIRATION_TIMEOUT);
275
+ refreshSameAddressEntry(sameAddress, service, src, source) {
276
+ const idx = this.services.indexOf(sameAddress);
277
+ const wasKnownDevicePlaceholder = sameAddress.source === 'known-device';
278
+ const nextSource = (wasKnownDevicePlaceholder && src !== 'known-device') ? src : sameAddress.source;
279
+ this.services[idx] = { ts: Date.now(), service, source: nextSource };
280
+ if (src !== 'known-device' && wasKnownDevicePlaceholder) {
281
+ this.logEvent({ message: 'device re-announced', device: service.name, announcement: service, source });
282
+ this.emitDevice(service);
283
+ }
284
+ }
285
+ announceNewEntry(service, src, source, message) {
286
+ this.services.push({ ts: Date.now(), service, source: src });
287
+ if (!service.serviceUUIDs?.length)
288
+ return;
289
+ this.logEvent({ message, device: service.name, announcement: service, source });
290
+ this.emitDevice(service);
291
+ this.matching?.push(service.name);
292
+ }
293
+ reannounceExistingEntry(existing, service, src, source) {
294
+ const idx = this.services.indexOf(existing);
295
+ this.services[idx] = { ts: Date.now(), service, source: src };
296
+ if (src !== 'known-device') {
297
+ this.logEvent({ message: 'device re-announced', device: service.name, announcement: service, source });
298
+ this.emitDevice(service);
299
+ }
300
+ }
301
+ findByNameAndAddress(name, address) {
302
+ return this.services.find(a => a.service.name === name && a.service.address === address && a.ts > Date.now() - DC_EXPIRATION_TIMEOUT);
303
+ }
304
+ findAllByName(name) {
305
+ return this.services.filter(a => a.service.name === name && a.ts > Date.now() - DC_EXPIRATION_TIMEOUT);
272
306
  }
273
307
  getAll() {
274
308
  return this.services.filter(a => a.ts > Date.now() - DC_EXPIRATION_TIMEOUT);
275
309
  }
310
+ hasNameCollision(name) {
311
+ return this.findAllByName(name).length > 1;
312
+ }
276
313
  setDebug(enabled) {
277
314
  this.debug = enabled;
278
315
  }
@@ -62,8 +62,13 @@ export default class DirectConnectInterface extends EventEmitter implements IBle
62
62
  name: string;
63
63
  }[];
64
64
  protected addService(service: MulticastDnsAnnouncement, source?: string): void;
65
- protected find(service: MulticastDnsAnnouncement): Announcement;
65
+ private refreshSameAddressEntry;
66
+ private announceNewEntry;
67
+ private reannounceExistingEntry;
68
+ protected findByNameAndAddress(name: string, address: string): Announcement;
69
+ protected findAllByName(name: string): Announcement[];
66
70
  protected getAll(): Announcement[];
71
+ hasNameCollision(name: string): boolean;
67
72
  setDebug(enabled: boolean): void;
68
73
  logEvent(event: any): void;
69
74
  logError(err: Error, fn: string, args?: any): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-devices",
3
- "version": "3.0.24",
3
+ "version": "3.0.25",
4
4
  "scripts": {
5
5
  "lint": "eslint . --ext .ts",
6
6
  "build": "npm run build:esm && npm run build:cjs",