esphome / aioesphomeapi

Python Client for ESPHome native API. Used by Home Assistant.

Home Page:https://esphome.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Can't connect after latest update "A Stream Socket was expected" Error

v-alvex opened this issue · comments

Traceback (most recent call last): File "C:\Users\Administrator\aio\aio\lib\site-packages\aioesphomeapi\client.py", line 176, in connect await self._connection.connect() File "C:\Users\Administrator\aio\aio\lib\site-packages\aioesphomeapi\connection.py", line 157, in connect self._socket_reader, self._socket_writer = await asyncio.open_connection( File "C:\Users\Administrator\AppData\Local\Programs\Python\Python38\lib\asyncio\streams.py", line 52, in open_connection transport, _ = await loop.create_connection( File "C:\Users\Administrator\AppData\Local\Programs\Python\Python38\lib\asyncio\base_events.py", line 1047, in create_connection raise ValueError( ValueError: A Stream Socket was expected, got <socket.socket fd=592, family=AddressFamily.AF_INET, type=0, proto=6, laddr=('192.168.2.4', 32149), raddr=('192.168.2.25', 6053)> The above exception was the direct cause of the following exception: Traceback (most recent call last): File "t.py", line 24, in <module> loop.run_until_complete(main()) File "C:\Users\Administrator\AppData\Local\Programs\Python\Python38\lib\asyncio\base_events.py", line 616, in run_until_complete return future.result() File "t.py", line 10, in main await api.connect(login=True) File "C:\Users\Administrator\aio\aio\lib\site-packages\aioesphomeapi\client.py", line 184, in connect raise APIConnectionError( aioesphomeapi.core.APIConnectionError: Unexpected error while connecting to 192.168.2.25: A Stream Socket was expected, got <socket.socket fd=592, family=AddressFamily.AF_INET, type=0, proto=6, laddr=('192.168.2.4', 32149), raddr=('192.168.2.25', 6053)>

After replacing, in line 132 of connection.py

self._socket = socket.socket( family=addr.family, type=addr.type, proto=addr.proto )

with

self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

The problem is fixed.

Hmm, looks like an issue with getaddrinfo on windows.

Could you insert this print in host_resolver:_async_resolve_host_getaddrinfo and report the result? Thanks!

async def _async_resolve_host_getaddrinfo(
    eventloop: asyncio.events.AbstractEventLoop, host: str, port: int
) -> List[AddrInfo]:
    try:
        # Limit to TCP IP protocol
        res = await eventloop.getaddrinfo(host, port, proto=socket.IPPROTO_TCP)
    except OSError as err:
        raise APIConnectionError("Error resolving IP address: {}".format(err))
    print(res)   # this line
    # ...

Sure here's the print result.
[(<AddressFamily.AF_INET: 2>, 0, 6, '', ('192.168.2.25', 6053))]

Thanks, that means getaddrinfo on windows apparently doesn't return a valid socket type.

Can you try if changing the getaddrinfo line with this works?

res = await eventloop.getaddrinfo(host, port, type=socket.SOCK_STREAM)

Yes it works now.
Here's the new print result.
[(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 0, '', ('192.168.2.25', 6053))]

Confirmed the above patch suggestion fixes it for me.

Windows 10, showing socket.getaddrinfo with and without specifying the type:

>>> socket.getaddrinfo("1.2.3.4", 1000, proto=socket.IPPROTO_TCP)
[(<AddressFamily.AF_INET: 2>, 0, 6, '', ('1.2.3.4', 1000))]

>>> socket.getaddrinfo("1.2.3.4", 1000, proto=socket.IPPROTO_TCP, type=socket.SOCK_STREAM)
[(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('1.2.3.4', 1000))]

Maybe this is a bug in Python 3 on Windows?

From the python docs:

socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)¶
...
The family, type and proto arguments can be optionally specified in order to narrow the list of addresses returned. Passing zero as a value for each of these arguments selects the full range of results.

Based on the above, the behavior on Linux seems correct, that when no type is given, it defaults to 0, which means select all possible results (limited by IPPROTO_TCP, perhaps, so only SOCK_STREAM is returned), yet on Windows 10, it returns a sockaddr with type=0.

Well on Windows if you pass in 0 in hints.ai_socktype you also get 0 back from getaddrinfo
"A value of zero for ai_socktype indicates the caller will accept any socket type."

My understanding is that you can't rely on the implementatopm to return SOCK_STREAM as the default type.
Can't tell if that if that is an implementation difference on Windows and Linux or an python artifact.

However SOCK_STREAM is enforced by the API Server there is no harm hardcoding it for client connections. The easiest fix is what @OttoWinter mentioned in #87 (comment)