def comports(include_links=False): # XXX include_links is currently ignored. are links in /dev even supported here? # Scan for all iokit serial ports services = GetIOServicesByType('IOSerialBSDClient') ports = [] serial_interfaces = scan_interfaces() for service in services: # First, add the callout device file. device = get_string_property(service, "IOCalloutDevice") if device: info = list_ports_common.ListPortInfo(device) # If the serial port is implemented by IOUSBDevice usb_device = GetParentDeviceByType(service, "IOUSBHostDevice") if not usb_device: # Backward compatibility, macOS 10.0-10.10: usb_device = GetParentDeviceByType(service, "IOUSBDevice") if usb_device: # fetch some useful informations from properties info.vid = get_int_property(usb_device, "idVendor", kCFNumberSInt16Type) info.pid = get_int_property(usb_device, "idProduct", kCFNumberSInt16Type) info.serial_number = get_string_property(usb_device, kUSBSerialNumberString) # We know this is a usb device, so the # IORegistryEntryName should always be aliased to the # usb product name string descriptor. info.product = IORegistryEntryGetName(usb_device) or 'n/a' info.manufacturer = get_string_property(usb_device, kUSBVendorString) locationID = get_int_property(usb_device, "locationID", kCFNumberSInt32Type) info.location = location_to_string(locationID) info.interface = search_for_locationID_in_interfaces(serial_interfaces, locationID) info.apply_usb_info() ports.append(info) return ports
def comports(): # Scan for all iokit serial ports services = GetIOServicesByType('IOSerialBSDClient') ports = [] serial_interfaces = scan_interfaces() for service in services: # First, add the callout device file. device = get_string_property(service, "IOCalloutDevice") if device: info = list_ports_common.ListPortInfo(device) # If the serial port is implemented by IOUSBDevice usb_device = GetParentDeviceByType(service, "IOUSBDevice") if usb_device: # fetch some useful informations from properties info.vid = get_int_property(usb_device, "idVendor", kCFNumberSInt16Type) info.pid = get_int_property(usb_device, "idProduct", kCFNumberSInt16Type) info.serial_number = get_string_property(usb_device, "USB Serial Number") info.product = get_string_property(usb_device, "USB Product Name") or 'n/a' info.manufacturer = get_string_property(usb_device, "USB Vendor Name") locationID = get_int_property(usb_device, "locationID", kCFNumberSInt32Type) info.location = location_to_string(locationID) info.interface = search_for_locationID_in_interfaces(serial_interfaces, locationID) info.apply_usb_info() ports.append(info) return ports
async def test_form(hass): """Test we get the form.""" await setup.async_setup_component(hass, "persistent_notification", {}) fakecomports = [] fakecomports.append(list_ports_common.ListPortInfo("/dev/ttyUSB7")) with patch( "serial.tools.list_ports.comports", return_value=fakecomports, ): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER}) assert result["type"] == "form" assert result["errors"] == {} with patch( "aurorapy.client.AuroraSerialClient.connect", return_value=None, ), patch( "aurorapy.client.AuroraSerialClient.serial_number", return_value="9876543", ), patch( "aurorapy.client.AuroraSerialClient.version", return_value="9.8.7.6", ), patch( "aurorapy.client.AuroraSerialClient.pn", return_value="A.B.C", ), patch( "aurorapy.client.AuroraSerialClient.firmware", return_value="1.234", ), patch( "homeassistant.components.aurora_abb_powerone.config_flow._LOGGER.getEffectiveLevel", return_value=INFO, ) as mock_setup, patch( "homeassistant.components.aurora_abb_powerone.async_setup_entry", return_value=True, ) as mock_setup_entry: result2 = await hass.config_entries.flow.async_configure( result["flow_id"], { CONF_PORT: "/dev/ttyUSB7", CONF_ADDRESS: 7 }, ) assert result2["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY assert result2["data"] == { CONF_PORT: "/dev/ttyUSB7", CONF_ADDRESS: 7, ATTR_FIRMWARE: "1.234", ATTR_MODEL: "9.8.7.6 (A.B.C)", ATTR_SERIAL_NUMBER: "9876543", "title": "PhotoVoltaic Inverters", } await hass.async_block_till_done() assert len(mock_setup.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
def regval_to_listport(winport): """Convert a windows port from registry key to pyserial's ListPortInfo. Args: winport (tuple): Windows registry value (description, device, value). Returns: listport (ListPortInfo): comport device details. """ # Create the ListPortInfo description, device, _ = winport listport = list_ports_common.ListPortInfo(device) # Format the description like other ListPortInfo description = description.replace('\\Device\\', '') listport.description = "{} ({})".format(description, device) return listport
def comports(): """scan for available ports. return a list of device names.""" devices = glob.glob('/dev/tty*') return [list_ports_common.ListPortInfo(d) for d in devices]
def comports(): devices = glob.glob('/dev/cua*[!.init][!.lock]') return [list_ports_common.ListPortInfo(d) for d in devices]
def comports(): devices = glob.glob('/dev/ttyS*') return [list_ports_common.ListPortInfo(d) for d in devices]
def iterate_comports(): """Return a generator that yields descriptions for serial ports""" PortsGUIDs = (GUID * 8)() # so far only seen one used, so hope 8 are enough... ports_guids_size = DWORD() if not SetupDiClassGuidsFromName("Ports", PortsGUIDs, ctypes.sizeof(PortsGUIDs), ctypes.byref(ports_guids_size)): raise ctypes.WinError() ModemsGUIDs = (GUID * 8)() # so far only seen one used, so hope 8 are enough... modems_guids_size = DWORD() if not SetupDiClassGuidsFromName("Modem", ModemsGUIDs, ctypes.sizeof(ModemsGUIDs), ctypes.byref(modems_guids_size)): raise ctypes.WinError() GUIDs = PortsGUIDs[:ports_guids_size. value] + ModemsGUIDs[:modems_guids_size.value] # repeat for all possible GUIDs for index in range(len(GUIDs)): bInterfaceNumber = None g_hdi = SetupDiGetClassDevs( ctypes.byref(GUIDs[index]), None, NULL, DIGCF_PRESENT ) # was DIGCF_PRESENT|DIGCF_DEVICEINTERFACE which misses CDC ports devinfo = SP_DEVINFO_DATA() devinfo.cbSize = ctypes.sizeof(devinfo) index = 0 while SetupDiEnumDeviceInfo(g_hdi, index, ctypes.byref(devinfo)): index += 1 # get the real com port name hkey = SetupDiOpenDevRegKey( g_hdi, ctypes.byref(devinfo), DICS_FLAG_GLOBAL, 0, DIREG_DEV, # DIREG_DRV for SW info KEY_READ) port_name_buffer = ctypes.create_unicode_buffer(250) port_name_length = ULONG(ctypes.sizeof(port_name_buffer)) RegQueryValueEx(hkey, "PortName", None, None, ctypes.byref(port_name_buffer), ctypes.byref(port_name_length)) 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 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 SetupDiGetDeviceRegistryProperty( g_hdi, ctypes.byref(devinfo), SPDRP_HARDWAREID, None, ctypes.byref(szHardwareID), ctypes.sizeof(szHardwareID) - 1, None): # Ignore ERROR_INSUFFICIENT_BUFFER if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER: raise ctypes.WinError() # stringify szHardwareID_str = szHardwareID.value info = list_ports_common.ListPortInfo(port_name_buffer.value, skip_link_detection=True) # in case of USB, make a more readable string, similar to that form # that we also generate on other platforms if szHardwareID_str.startswith('USB'): m = re.search( r'VID_([0-9a-f]{4})(&PID_([0-9a-f]{4}))?(&MI_(\d{2}))?(\\(.*))?', szHardwareID_str, re.I) if m: info.vid = int(m.group(1), 16) if m.group(3): info.pid = int(m.group(3), 16) if m.group(5): bInterfaceNumber = int(m.group(5)) # Check that the USB serial number only contains alpha-numeric characters. It # may be a windows device ID (ephemeral ID) for composite devices. if m.group(7) and re.match(r'^\w+$', m.group(7)): info.serial_number = m.group(7) else: info.serial_number = get_parent_serial_number( devinfo.DevInst, info.vid, info.pid) # calculate a location string loc_path_str = ctypes.create_unicode_buffer(250) if SetupDiGetDeviceRegistryProperty( g_hdi, ctypes.byref(devinfo), SPDRP_LOCATION_PATHS, None, ctypes.byref(loc_path_str), ctypes.sizeof(loc_path_str) - 1, None): m = re.finditer(r'USBROOT\((\w+)\)|#USB\((\w+)\)', loc_path_str.value) location = [] for g in m: if g.group(1): location.append('{:d}'.format(int(g.group(1)) + 1)) else: if len(location) > 1: location.append('.') else: location.append('-') location.append(g.group(2)) if bInterfaceNumber is not None: location.append(':{}.{}'.format( 'x', # XXX how to determine correct bConfigurationValue? bInterfaceNumber)) if location: info.location = ''.join(location) info.hwid = info.usb_info() elif szHardwareID_str.startswith('FTDIBUS'): m = re.search( r'VID_([0-9a-f]{4})\+PID_([0-9a-f]{4})(\+(\w+))?', szHardwareID_str, re.I) if m: info.vid = int(m.group(1), 16) info.pid = int(m.group(2), 16) if m.group(4): info.serial_number = m.group(4) # USB location is hidden by FDTI driver :( info.hwid = info.usb_info() else: info.hwid = szHardwareID_str # friendly name szFriendlyName = ctypes.create_unicode_buffer(250) if SetupDiGetDeviceRegistryProperty( g_hdi, ctypes.byref(devinfo), SPDRP_FRIENDLYNAME, #~ SPDRP_DEVICEDESC, None, ctypes.byref(szFriendlyName), ctypes.sizeof(szFriendlyName) - 1, None): info.description = szFriendlyName.value #~ else: # Ignore ERROR_INSUFFICIENT_BUFFER #~ if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER: #~ raise IOError("failed to get details for %s (%s)" % (devinfo, szHardwareID.value)) # ignore errors and still include the port in the list, friendly name will be same as port name # manufacturer szManufacturer = ctypes.create_unicode_buffer(250) if SetupDiGetDeviceRegistryProperty( g_hdi, ctypes.byref(devinfo), SPDRP_MFG, #~ SPDRP_DEVICEDESC, None, ctypes.byref(szManufacturer), ctypes.sizeof(szManufacturer) - 1, None): info.manufacturer = szManufacturer.value yield info SetupDiDestroyDeviceInfoList(g_hdi)
#import usb #import serial import serial.tools.list_ports as lp import serial.tools.list_ports_common as lpc all_ports = lp.comports() my_filter = lambda port: 'thorlabs' in port.manufacturer.lower() all_ports = lp.comports() my_ports = [port for port in all_ports if my_filter(port)] #usb_devices = usb.core.find(find_all=True) #for device in usb_devices: # if device.bDeviceClass!=9: # try: # print(device.manufacturer) # except ValueError: # device._langids = (1033,) for port in my_ports : print(port.device,port.serial_number) wincomports = ['COM'+str(item) for item in range(256)] for port in wincomports: p = lpc.ListPortInfo(port) print(p.manufacturer)
def comports(): GUIDs = (GUID * 8)() # so far only seen one used, so hope 8 are enough... guids_size = DWORD() if not 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): g_hdi = SetupDiGetClassDevs( ctypes.byref(GUIDs[index]), None, NULL, DIGCF_PRESENT ) # was DIGCF_PRESENT|DIGCF_DEVICEINTERFACE which misses CDC ports devinfo = SP_DEVINFO_DATA() devinfo.cbSize = ctypes.sizeof(devinfo) index = 0 while SetupDiEnumDeviceInfo(g_hdi, index, ctypes.byref(devinfo)): index += 1 # get the real com port name hkey = SetupDiOpenDevRegKey( g_hdi, ctypes.byref(devinfo), DICS_FLAG_GLOBAL, 0, DIREG_DEV, # DIREG_DRV for SW info KEY_READ) port_name_buffer = byte_buffer(250) port_name_length = ULONG(ctypes.sizeof(port_name_buffer)) RegQueryValueEx(hkey, PortName, None, None, ctypes.byref(port_name_buffer), ctypes.byref(port_name_length)) 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 string(port_name_buffer).startswith('LPT'): continue # hardware ID szHardwareID = byte_buffer(250) # try to get ID that includes serial number if not SetupDiGetDeviceInstanceId( g_hdi, ctypes.byref(devinfo), ctypes.byref(szHardwareID), ctypes.sizeof(szHardwareID) - 1, None): # fall back to more generic hardware ID if that would fail if not SetupDiGetDeviceRegistryProperty( g_hdi, ctypes.byref(devinfo), SPDRP_HARDWAREID, None, ctypes.byref(szHardwareID), ctypes.sizeof(szHardwareID) - 1, None): # Ignore ERROR_INSUFFICIENT_BUFFER if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER: raise ctypes.WinError() # stringify szHardwareID_str = string(szHardwareID) info = list_ports_common.ListPortInfo(string(port_name_buffer)) # in case of USB, make a more readable string, similar to that form # that we also generate on other platforms if szHardwareID_str.startswith('USB'): m = re.search(r'VID_([0-9a-f]{4})&PID_([0-9a-f]{4})(\\(\w+))?', szHardwareID_str, re.I) if m: info.vid = int(m.group(1), 16) info.pid = int(m.group(2), 16) if m.group(4): info.serial_number = m.group(4) # calculate a location string # XXX was empty in tests with (internal) USB3 hub :( loc_path_str = byte_buffer(250) if SetupDiGetDeviceRegistryProperty( g_hdi, ctypes.byref(devinfo), SPDRP_LOCATION_PATHS, None, ctypes.byref(loc_path_str), ctypes.sizeof(loc_path_str) - 1, None): #~ print (string(loc_path_str)) m = re.finditer(r'USBROOT\((\w+)\)|#USB\((\w+)\)', string(loc_path_str)) location = [] for g in m: if g.group(1): location.append('%d' % (int(g.group(1)) + 1)) else: if len(location) > 1: location.append('.') else: location.append('-') location.append(g.group(2)) if location: info.location = ''.join(location) info.hwid = info.usb_info() else: info.hwid = szHardwareID_str # friendly name szFriendlyName = byte_buffer(250) if SetupDiGetDeviceRegistryProperty( g_hdi, ctypes.byref(devinfo), SPDRP_FRIENDLYNAME, #~ SPDRP_DEVICEDESC, None, ctypes.byref(szFriendlyName), ctypes.sizeof(szFriendlyName) - 1, None): info.description = string(szFriendlyName) #~ else: # Ignore ERROR_INSUFFICIENT_BUFFER #~ if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER: #~ raise IOError("failed to get details for %s (%s)" % (devinfo, szHardwareID.value)) # ignore errors and still include the port in the list, friendly name will be same as port name yield info SetupDiDestroyDeviceInfoList(g_hdi)
def comports(include_links=False): """scan for available ports. return a list of device names.""" devices = set(glob.glob('/dev/tty*')) if include_links: devices.update(list_ports_common.list_links(devices)) return [list_ports_common.ListPortInfo(d) for d in devices]
def comports(include_links=False): devices = set(glob.glob('/dev/cua*[!.init][!.lock]')) if include_links: devices.update(list_ports_common.list_links(devices)) return [list_ports_common.ListPortInfo(d) for d in devices]
def comports(include_links=False): devices = glob.glob('/dev/cua*') if include_links: devices.extend(list_ports_common.list_links(devices)) return [list_ports_common.ListPortInfo(d) for d in devices]
async def test_form_invalid_com_ports(hass): """Test we display correct info when the comport is invalid..""" fakecomports = [] fakecomports.append(list_ports_common.ListPortInfo("/dev/ttyUSB7")) with patch( "serial.tools.list_ports.comports", return_value=fakecomports, ): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER}) assert result["type"] == "form" assert result["errors"] == {} with patch( "aurorapy.client.AuroraSerialClient.connect", side_effect=OSError(19, "...no such device..."), return_value=None, ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], { CONF_PORT: "/dev/ttyUSB7", CONF_ADDRESS: 7 }, ) assert result2["errors"] == {"base": "invalid_serial_port"} with patch( "aurorapy.client.AuroraSerialClient.connect", side_effect=AuroraError("..could not open port..."), return_value=None, ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], { CONF_PORT: "/dev/ttyUSB7", CONF_ADDRESS: 7 }, ) assert result2["errors"] == {"base": "cannot_open_serial_port"} with patch( "aurorapy.client.AuroraSerialClient.connect", side_effect=AuroraTimeoutError("...No response after..."), return_value=None, ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], { CONF_PORT: "/dev/ttyUSB7", CONF_ADDRESS: 7 }, ) assert result2["errors"] == {"base": "cannot_connect"} with patch( "aurorapy.client.AuroraSerialClient.connect", side_effect=AuroraError("...Some other message!!!123..."), return_value=None, ), patch( "serial.Serial.isOpen", return_value=True, ), patch("aurorapy.client.AuroraSerialClient.close", ) as mock_clientclose: result2 = await hass.config_entries.flow.async_configure( result["flow_id"], { CONF_PORT: "/dev/ttyUSB7", CONF_ADDRESS: 7 }, ) assert result2["errors"] == {"base": "cannot_connect"} assert len(mock_clientclose.mock_calls) == 1
async def test_import_night_then_user(hass): """Attempt yaml import and fail (dark), but user sets up manually before auto retry.""" TEST_DATA = {"device": "/dev/ttyUSB7", "address": 3, "name": "MyAuroraPV"} # First time round, no response. with patch( "aurorapy.client.AuroraSerialClient.connect", side_effect=AuroraError("No response after"), ) as mock_connect: result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data=TEST_DATA) configs = hass.config_entries.async_entries(DOMAIN) assert len(configs) == 1 entry = configs[0] assert not entry.unique_id assert entry.state == ConfigEntryState.SETUP_RETRY assert len(mock_connect.mock_calls) == 1 assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result["data"][CONF_PORT] == "/dev/ttyUSB7" assert result["data"][CONF_ADDRESS] == 3 # Failed once, now simulate the user initiating config flow with valid settings. fakecomports = [] fakecomports.append(list_ports_common.ListPortInfo("/dev/ttyUSB7")) with patch( "serial.tools.list_ports.comports", return_value=fakecomports, ): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER}) assert result["type"] == "form" assert result["errors"] == {} with patch( "aurorapy.client.AuroraSerialClient.connect", return_value=None, ), patch( "aurorapy.client.AuroraSerialClient.serial_number", return_value="9876543", ), patch( "aurorapy.client.AuroraSerialClient.version", return_value="9.8.7.6", ), patch( "aurorapy.client.AuroraSerialClient.pn", return_value="A.B.C", ), patch( "aurorapy.client.AuroraSerialClient.firmware", return_value="1.234", ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], { CONF_PORT: "/dev/ttyUSB7", CONF_ADDRESS: 7 }, ) assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert len(hass.config_entries.async_entries(DOMAIN)) == 2 # Now retry yaml - it should fail with duplicate with patch( "aurorapy.client.AuroraSerialClient.connect", return_value=None, ), patch( "aurorapy.client.AuroraSerialClient.serial_number", return_value="9876543", ), patch( "aurorapy.client.AuroraSerialClient.version", return_value="9.8.7.6", ), patch( "aurorapy.client.AuroraSerialClient.pn", return_value="A.B.C", ), patch( "aurorapy.client.AuroraSerialClient.firmware", return_value="1.234", ): # Wait >5seconds for the config to auto retry. async_fire_time_changed(hass, utcnow() + timedelta(seconds=6)) await hass.async_block_till_done() assert entry.state == ConfigEntryState.NOT_LOADED assert len(hass.config_entries.async_entries(DOMAIN)) == 1