На самом деле в ListPortInfo есть поле location , возвращаемое serial.tools.list_ports.comports . Однако похоже, что windows реализация list_ports (list_ports_ windows .py), похоже, не может правильно восстановить эту информацию. Я использовал модуль регистрации, чтобы исследовать это, добавив следующую строку в мой pyserial (установлен на win10 64, python3 .8). Добавление
logging.debug("szHardwareID_str: %s" % szHardwareID_str) #added this lin
после
# stringify
szHardwareID_str = szHardwareID.value
принесло мне:
DEBUG:root:szHardwareID_str: ACPI\PNP0501\0
DEBUG:root:szHardwareID_str: FTDIBUS\VID_0403+PID_6010+6&192CD50C&0&13&2\0000
DEBUG:root:szHardwareID_str: FTDIBUS\VID_0403+PID_6010+6&192CD50C&0&13&1\0000
Итак, можно увидеть, что местоположение на самом деле находится внутри этой строки, но более поздний анализ строки не удается правильно извлечь эту информацию. Т.е. реализованный поиск не соответствует locationID:
m = re.search(r'VID_([0-9a-f]{4})(&PID_([0-9a-f]{4}))?(&MI_(\d{2}))?(\\(\w+))?', szHardwareID_str, re.I)
Короче говоря: насколько мне известно, вы можете модифицировать pyserial (локально или глобально, создавая patch + pull) запрос) или вы вручную «извлекаете» информацию с помощью модуля pyserial.tools.list_ports. Например что-то вроде:
import ctypes
import serial
import logging
import serial.tools.list_ports_windows as lp
logging.basicConfig(level=logging.DEBUG)
################copied from list_ports_windows.py
GUIDs = (lp.GUID * 8)() # so far only seen one used, so hope 8 are enough...
guids_size = lp.DWORD()
if not lp.SetupDiClassGuidsFromName(
"Ports",
GUIDs,
ctypes.sizeof(GUIDs),
ctypes.byref(guids_size)):
raise ctypes.WinError()
# repeat for all possible GUIDs
for index in range(guids_size.value):
bInterfaceNumber = None
g_hdi = lp.SetupDiGetClassDevs(
ctypes.byref(GUIDs[index]),
None,
lp.NULL,
lp.DIGCF_PRESENT) # was DIGCF_PRESENT|DIGCF_DEVICEINTERFACE which misses CDC ports
devinfo = lp.SP_DEVINFO_DATA()
devinfo.cbSize = ctypes.sizeof(devinfo)
index = 0
while lp.SetupDiEnumDeviceInfo(g_hdi, index, ctypes.byref(devinfo)):
index += 1
# get the real com port name
hkey = lp.SetupDiOpenDevRegKey(
g_hdi,
ctypes.byref(devinfo),
lp.DICS_FLAG_GLOBAL,
0,
lp.DIREG_DEV, # DIREG_DRV for SW info
lp.KEY_READ)
port_name_buffer = ctypes.create_unicode_buffer(250)
port_name_length = lp.ULONG(ctypes.sizeof(port_name_buffer))
lp.RegQueryValueEx(
hkey,
"PortName",
None,
None,
ctypes.byref(port_name_buffer),
ctypes.byref(port_name_length))
lp.RegCloseKey(hkey)
# unfortunately does this method also include parallel ports.
# we could check for names starting with COM or just exclude LPT
# and hope that other "unknown" names are serial ports...
if port_name_buffer.value.startswith('LPT'):
continue
# hardware ID
szHardwareID = ctypes.create_unicode_buffer(250)
# try to get ID that includes serial number
if not lp.SetupDiGetDeviceInstanceId(
g_hdi,
ctypes.byref(devinfo),
#~ ctypes.byref(szHardwareID),
szHardwareID,
ctypes.sizeof(szHardwareID) - 1,
None):
# fall back to more generic hardware ID if that would fail
if not lp.SetupDiGetDeviceRegistryProperty(
g_hdi,
ctypes.byref(devinfo),
lp.SPDRP_HARDWAREID,
None,
ctypes.byref(szHardwareID),
ctypes.sizeof(szHardwareID) - 1,
None):
# Ignore ERROR_INSUFFICIENT_BUFFER
if ctypes.GetLastError() != lp.ERROR_INSUFFICIENT_BUFFER:
raise ctypes.WinError()
# stringify
szHardwareID_str = szHardwareID.value
print(szHardwareID_str)
Однако общее решение (например, патч) будет лучшим способом ИМХО. Может быть, вы могли бы просто открыть номер в официальных репозиториях Pyserial.