@mcesystems/usb-device-listener 1.0.31 → 1.0.32

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.
package/dist/types.d.ts CHANGED
@@ -19,6 +19,12 @@ export interface DeviceInfo {
19
19
  * Empty string if the device doesn't expose a serial number
20
20
  */
21
21
  serialNumber: string;
22
+ /**
23
+ * USB product name / device description
24
+ * Examples: "iPhone", "SAMSUNG_Android", "Arduino Uno"
25
+ * Empty string if not available
26
+ */
27
+ deviceName: string;
22
28
  /**
23
29
  * USB Vendor ID (decimal)
24
30
  * Use .toString(16) to convert to hex string
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAExC;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAE/B;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC;IAEhC;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,EAAE,YAAY,EAAE,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;AAEpE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC;;;;;;;;;;;;;;;;OAgBG;IACH,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAE7C;;;;;;;;OAQG;IACH,aAAa,IAAI,IAAI,CAAC;IAEtB;;;;;;;;;;;;;;OAcG;IACH,WAAW,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAE/C;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAErD;;;;;;;;;;;;;;OAcG;IACH,WAAW,IAAI,UAAU,EAAE,CAAC;CAC5B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAExC;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAE/B;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC;IAEhC;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,EAAE,YAAY,EAAE,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;AAEpE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC;;;;;;;;;;;;;;;;OAgBG;IACH,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAE7C;;;;;;;;OAQG;IACH,aAAa,IAAI,IAAI,CAAC;IAEtB;;;;;;;;;;;;;;OAcG;IACH,WAAW,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAE/C;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAErD;;;;;;;;;;;;;;OAcG;IACH,WAAW,IAAI,UAAU,EAAE,CAAC;CAC5B"}
package/native/addon.cc CHANGED
@@ -98,6 +98,7 @@ Napi::Value ListDevices(const Napi::CallbackInfo& info) {
98
98
  Napi::Object obj = Napi::Object::New(env);
99
99
  obj.Set("deviceId", Napi::String::New(env, devices[i].deviceId));
100
100
  obj.Set("serialNumber", Napi::String::New(env, devices[i].serialNumber));
101
+ obj.Set("deviceName", Napi::String::New(env, devices[i].deviceName));
101
102
  obj.Set("vid", Napi::Number::New(env, devices[i].vid));
102
103
  obj.Set("pid", Napi::Number::New(env, devices[i].pid));
103
104
  obj.Set("locationInfo", Napi::String::New(env, devices[i].locationInfo));
@@ -16,6 +16,7 @@
16
16
  struct DeviceInfo {
17
17
  std::string deviceId; // Device ID (serial number if available, otherwise platform-specific)
18
18
  std::string serialNumber; // USB serial number (raw, without formatting)
19
+ std::string deviceName; // USB product name / device description
19
20
  uint16_t vid{0}; // Vendor ID
20
21
  uint16_t pid{0}; // Product ID
21
22
  std::string locationInfo; // Physical port location (platform-specific format)
@@ -26,39 +26,72 @@ static const char* GetUSBDeviceClassName() {
26
26
  }
27
27
 
28
28
  /**
29
- * Get the USB serial number from the IOKit service
30
- * Returns empty string if serial number is not available
29
+ * Get a string property from an IOKit service
30
+ * Returns empty string if the property is not available
31
31
  */
32
- static std::string GetSerialNumber(io_service_t service) {
33
- // Try kUSBSerialNumberString first (standard USB property)
34
- CFStringRef serialRef = (CFStringRef)IORegistryEntryCreateCFProperty(
35
- service, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, 0);
32
+ static std::string GetStringProperty(io_service_t service, CFStringRef propertyName) {
33
+ CFStringRef stringRef = (CFStringRef)IORegistryEntryCreateCFProperty(
34
+ service, propertyName, kCFAllocatorDefault, 0);
36
35
 
37
- // Fallback to "USB Serial Number" property name
38
- if (!serialRef) {
39
- serialRef = (CFStringRef)IORegistryEntryCreateCFProperty(
40
- service, CFSTR("USB Serial Number"), kCFAllocatorDefault, 0);
41
- }
42
-
43
- if (!serialRef) {
36
+ if (!stringRef) {
44
37
  return "";
45
38
  }
46
39
 
47
40
  // Convert CFString to std::string
48
- CFIndex length = CFStringGetLength(serialRef);
41
+ CFIndex length = CFStringGetLength(stringRef);
49
42
  CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
50
43
  std::string result(maxSize, '\0');
51
44
 
52
- if (CFStringGetCString(serialRef, &result[0], maxSize, kCFStringEncodingUTF8)) {
45
+ if (CFStringGetCString(stringRef, &result[0], maxSize, kCFStringEncodingUTF8)) {
53
46
  result.resize(strlen(result.c_str()));
54
47
  } else {
55
48
  result.clear();
56
49
  }
57
50
 
58
- CFRelease(serialRef);
51
+ CFRelease(stringRef);
59
52
  return result;
60
53
  }
61
54
 
55
+ /**
56
+ * Get the USB product name / device name from the IOKit service
57
+ * Returns empty string if not available
58
+ */
59
+ static std::string GetDeviceName(io_service_t service) {
60
+ // Try kUSBProductString first (standard USB property)
61
+ std::string name = GetStringProperty(service, CFSTR(kUSBProductString));
62
+
63
+ // Fallback to "USB Product Name"
64
+ if (name.empty()) {
65
+ name = GetStringProperty(service, CFSTR("USB Product Name"));
66
+ }
67
+
68
+ // Fallback to IOKit device name
69
+ if (name.empty()) {
70
+ io_name_t deviceName;
71
+ if (IORegistryEntryGetName(service, deviceName) == KERN_SUCCESS) {
72
+ name = deviceName;
73
+ }
74
+ }
75
+
76
+ return name;
77
+ }
78
+
79
+ /**
80
+ * Get the USB serial number from the IOKit service
81
+ * Returns empty string if serial number is not available
82
+ */
83
+ static std::string GetSerialNumber(io_service_t service) {
84
+ // Try kUSBSerialNumberString first (standard USB property)
85
+ std::string serial = GetStringProperty(service, CFSTR(kUSBSerialNumberString));
86
+
87
+ // Fallback to "USB Serial Number" property name
88
+ if (serial.empty()) {
89
+ serial = GetStringProperty(service, CFSTR("USB Serial Number"));
90
+ }
91
+
92
+ return serial;
93
+ }
94
+
62
95
  /**
63
96
  * Format serial number in iOS-style dash-separated format
64
97
  * iOS UDIDs are typically 24-25 characters and formatted as: XXXXXXXX-XXXXXXXXXXXXXXXX
@@ -290,6 +323,7 @@ void USBListener::HandleDeviceAdded(io_iterator_t iterator) {
290
323
  Napi::Object obj = Napi::Object::New(env);
291
324
  obj.Set("deviceId", Napi::String::New(env, data->deviceId));
292
325
  obj.Set("serialNumber", Napi::String::New(env, data->serialNumber));
326
+ obj.Set("deviceName", Napi::String::New(env, data->deviceName));
293
327
  obj.Set("vid", Napi::Number::New(env, data->vid));
294
328
  obj.Set("pid", Napi::Number::New(env, data->pid));
295
329
  obj.Set("locationInfo", Napi::String::New(env, data->locationInfo));
@@ -342,6 +376,7 @@ void USBListener::HandleDeviceRemoved(io_iterator_t iterator) {
342
376
  Napi::Object obj = Napi::Object::New(env);
343
377
  obj.Set("deviceId", Napi::String::New(env, data->deviceId));
344
378
  obj.Set("serialNumber", Napi::String::New(env, data->serialNumber));
379
+ obj.Set("deviceName", Napi::String::New(env, data->deviceName));
345
380
  obj.Set("vid", Napi::Number::New(env, data->vid));
346
381
  obj.Set("pid", Napi::Number::New(env, data->pid));
347
382
  obj.Set("locationInfo", Napi::String::New(env, data->locationInfo));
@@ -401,6 +436,9 @@ bool USBListener::GetDeviceInfoFromService(io_service_t service, DeviceInfo& inf
401
436
  // Get USB serial number
402
437
  info.serialNumber = GetSerialNumber(service);
403
438
 
439
+ // Get device name
440
+ info.deviceName = GetDeviceName(service);
441
+
404
442
  // Use serial number as device ID if available (formatted for iOS style)
405
443
  // Otherwise fall back to VID/PID/location combination
406
444
  if (!info.serialNumber.empty()) {
@@ -188,6 +188,7 @@ void USBListener::HandleDeviceChange(WPARAM wParam, LPARAM lParam) {
188
188
  Napi::Object obj = Napi::Object::New(env);
189
189
  obj.Set("deviceId", Napi::String::New(env, data->deviceId));
190
190
  obj.Set("serialNumber", Napi::String::New(env, data->serialNumber));
191
+ obj.Set("deviceName", Napi::String::New(env, data->deviceName));
191
192
  obj.Set("vid", Napi::Number::New(env, data->vid));
192
193
  obj.Set("pid", Napi::Number::New(env, data->pid));
193
194
  obj.Set("locationInfo", Napi::String::New(env, data->locationInfo));
@@ -205,6 +206,7 @@ void USBListener::HandleDeviceChange(WPARAM wParam, LPARAM lParam) {
205
206
  Napi::Object obj = Napi::Object::New(env);
206
207
  obj.Set("deviceId", Napi::String::New(env, data->deviceId));
207
208
  obj.Set("serialNumber", Napi::String::New(env, data->serialNumber));
209
+ obj.Set("deviceName", Napi::String::New(env, data->deviceName));
208
210
  obj.Set("vid", Napi::Number::New(env, data->vid));
209
211
  obj.Set("pid", Napi::Number::New(env, data->pid));
210
212
  obj.Set("locationInfo", Napi::String::New(env, data->locationInfo));
@@ -290,6 +292,9 @@ bool USBListener::GetDeviceInfo(const std::string& devicePath, DeviceInfo& info)
290
292
  info.deviceId = fullDeviceId;
291
293
  }
292
294
 
295
+ // Get device name
296
+ info.deviceName = GetDeviceName(deviceInfoSet, devInfoData);
297
+
293
298
  SetupDiDestroyDeviceInfoList(deviceInfoSet);
294
299
  return true;
295
300
  }
@@ -334,6 +339,29 @@ bool USBListener::GetVidPid(const std::string& deviceId, uint16_t& vid, uint16_t
334
339
  }
335
340
  }
336
341
 
342
+ /**
343
+ * Get device description/name from Windows registry
344
+ */
345
+ std::string USBListener::GetDeviceName(HDEVINFO deviceInfoSet, SP_DEVINFO_DATA& devInfoData) {
346
+ WCHAR buffer[MAX_PATH];
347
+
348
+ // Try to get device description first (usually the friendly name)
349
+ if (SetupDiGetDeviceRegistryPropertyW(deviceInfoSet, &devInfoData, SPDRP_DEVICEDESC,
350
+ nullptr, reinterpret_cast<PBYTE>(buffer), sizeof(buffer), nullptr)) {
351
+ std::wstring nameW = buffer;
352
+ return std::string(nameW.begin(), nameW.end());
353
+ }
354
+
355
+ // Fallback to friendly name
356
+ if (SetupDiGetDeviceRegistryPropertyW(deviceInfoSet, &devInfoData, SPDRP_FRIENDLYNAME,
357
+ nullptr, reinterpret_cast<PBYTE>(buffer), sizeof(buffer), nullptr)) {
358
+ std::wstring nameW = buffer;
359
+ return std::string(nameW.begin(), nameW.end());
360
+ }
361
+
362
+ return "";
363
+ }
364
+
337
365
  /**
338
366
  * Get USB serial number from device instance ID
339
367
  * The serial number is typically the last part after the last backslash
@@ -416,6 +444,9 @@ void USBListener::EnumerateConnectedDevices() {
416
444
  info.deviceId = fullDeviceId;
417
445
  }
418
446
 
447
+ // Get device name
448
+ info.deviceName = GetDeviceName(deviceInfoSet, devInfoData);
449
+
419
450
  // Cache device with exclusive lock
420
451
  {
421
452
  std::unique_lock lock(m_cacheMutex);
@@ -427,6 +458,7 @@ void USBListener::EnumerateConnectedDevices() {
427
458
  Napi::Object obj = Napi::Object::New(env);
428
459
  obj.Set("deviceId", Napi::String::New(env, data->deviceId));
429
460
  obj.Set("serialNumber", Napi::String::New(env, data->serialNumber));
461
+ obj.Set("deviceName", Napi::String::New(env, data->deviceName));
430
462
  obj.Set("vid", Napi::Number::New(env, data->vid));
431
463
  obj.Set("pid", Napi::Number::New(env, data->pid));
432
464
  obj.Set("locationInfo", Napi::String::New(env, data->locationInfo));
@@ -481,6 +513,9 @@ std::vector<DeviceInfo> USBListener::ListAllDevices() {
481
513
  info.deviceId = fullDeviceId;
482
514
  }
483
515
 
516
+ // Get device name
517
+ info.deviceName = GetDeviceName(deviceInfoSet, devInfoData);
518
+
484
519
  devices.push_back(info);
485
520
  }
486
521
  }
@@ -75,6 +75,7 @@ private:
75
75
  bool GetDeviceInfoFromPath(const std::string& devicePath, DeviceInfo& info); // Parse device info from path (for disconnections)
76
76
  bool GetLocationInfo(DEVINST devInst, std::string& locationInfo); // Get physical port location
77
77
  std::string GetSerialNumber(HDEVINFO deviceInfoSet, SP_DEVINFO_DATA& devInfoData); // Get USB serial number
78
+ std::string GetDeviceName(HDEVINFO deviceInfoSet, SP_DEVINFO_DATA& devInfoData); // Get device description/name
78
79
  bool GetVidPid(const std::string& deviceId, uint16_t& vid, uint16_t& pid); // Parse VID/PID from device ID string
79
80
  void EnumerateConnectedDevices(); // Enumerate devices on startup
80
81
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcesystems/usb-device-listener",
3
- "version": "1.0.31",
3
+ "version": "1.0.32",
4
4
  "description": "Native cross-platform USB device listener for Windows and macOS",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",