Example #1
0
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
Example #2
0
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
Example #3
0
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
Example #4
0
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]
Example #8
0
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)
Example #9
0
#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)
Example #10
0
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)
Example #11
0
 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]
Example #12
0
 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]
Example #13
0
 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]
Example #14
0
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
Example #15
0
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