Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Garage door state stuck at closing or opening #143

Closed
HuidaeCho opened this issue Mar 15, 2022 · 2 comments · Fixed by #149
Closed

Garage door state stuck at closing or opening #143

HuidaeCho opened this issue Mar 15, 2022 · 2 comments · Fixed by #149

Comments

@HuidaeCho
Copy link

HuidaeCho commented Mar 15, 2022

This SO question is related.

I have two versions of almost the same code: (1) one using Bottle as a uWSGI web app, (2) another as a command line utility. The web app only works the first time after it's loaded and it fails with a "Session is closed" error thereafter. It reports the door state as "opening" or "closing" when the door is fully open or closed already. The command line program reports the correct state and works every time without any issues.

The web app used to work fine with pymyq 3.0.3 before I updated pymyq to 3.1.4 this morning.

I tested the same code with pymyq 3.0.3 again. Well, it works now again.

This is the core part of the app:

def do(command):
    print("command:", command)
    asyncio.get_event_loop().run_until_complete(do_myq(command))
    print("completed")

async def do_myq(command) -> None:
    async with ClientSession() as websession:
        try:
            api = await login(myq_user, myq_pass, websession)
            door = api.devices[next(iter(api.covers.keys()))]
            print("state:", door.state)
            if command == "toggle":
                if door.state == "closed":
                    print("task: open")
                    state = await door.open(wait_for_state=False)
                else:
                    print("task: close")
                    state = await door.close(wait_for_state=False)
            elif command == "open" and door.state == "closed":
                print("task: open")
                state = await door.open(wait_for_state=False)
            elif command == "close" and door.state == "open":
                print("task: close")
                state = await door.close(wait_for_state=False)
            else:
                state = None
            if state is not None:
                print("new state:", state, door.state)
        except Exception as e:
            traceback.print_exc(file=sys.stdout)

Actually, pymyq 3.0.3 also seems to raise the same "Session is closed" exception at login(), but Task exception was never retrieved (not sure what it means and what it's for). With pymyq 3.1.4, the same exception is raised when door.close() is called.

  1. Why is a fresh session closed?
  2. How can pymyq 3.0.3 work when the session is closed?
  3. Why doesn't it happen with the command line code?

With pymyq 3.1.4:

command: close
state: opening
task: close
Traceback (most recent call last):
  File "garagedoor.py", line 86, in do_myq
    state = await door.close(wait_for_state=False)
  File "/home/user/usr/local/lib/python3.9/site-packages/pymyq/garagedoor.py", line 66, in close
    return await self._send_state_command(
  File "/home/user/usr/local/lib/python3.9/site-packages/pymyq/device.py", line 190, in _send_state_command
    await self._wait_for_state_task
  File "/home/user/usr/local/lib/python3.9/site-packages/pymyq/device.py", line 261, in wait_for_state
    await self._account.update()
  File "/home/user/usr/local/lib/python3.9/site-packages/pymyq/account.py", line 194, in update
    await self._get_devices()
  File "/home/user/usr/local/lib/python3.9/site-packages/pymyq/account.py", line 99, in _get_devices
    _, devices_resp = await self._api.request(
  File "/home/user/usr/local/lib/python3.9/site-packages/pymyq/api.py", line 250, in request
    return await call_method(
  File "/home/user/usr/local/lib/python3.9/site-packages/pymyq/request.py", line 244, in request_json
    resp = await self._send_request(
  File "/home/user/usr/local/lib/python3.9/site-packages/pymyq/request.py", line 141, in _send_request
    resp = await websession.request(
  File "/home/user/usr/local/lib/python3.9/site-packages/aiohttp/client.py", line 399, in _request
    raise RuntimeError("Session is closed")
RuntimeError: Session is closed

With pymyq 3.0.3:

command: close
Task exception was never retrieved
future: <Task finished name='Task-51' coro=<MyQDevice.wait_for_state() done, defined at /home/user/usr/local/lib/python3.9/site-packages/pymyq/device.py:123> exception=RuntimeError('Session is closed')>
Traceback (most recent call last):
  File "/home/user/usr/local/lib/python3.9/site-packages/pymyq/device.py", line 141, in wait_for_state
    await self._api.update_device_info(for_account=self.account)
  File "/home/user/usr/local/lib/python3.9/site-packages/pymyq/api.py", line 634, in update_device_info
    await self._get_devices_for_account(account=account)
  File "/home/user/usr/local/lib/python3.9/site-packages/pymyq/api.py", line 513, in _get_devices_for_account
    _, devices_resp = await self.request(
  File "/home/user/usr/local/lib/python3.9/site-packages/pymyq/api.py", line 215, in request
    return await call_method(
  File "/home/user/usr/local/lib/python3.9/site-packages/pymyq/request.py", line 101, in request_json
    resp = await self._send_request(
  File "/home/user/usr/local/lib/python3.9/site-packages/pymyq/request.py", line 51, in _send_request
    resp = await websession.request(
  File "/home/user/usr/local/lib/python3.9/site-packages/aiohttp/client.py", line 399, in _request
    raise RuntimeError("Session is closed")
RuntimeError: Session is closed
state: open
task: close
new state: <Task pending name='Task-68' coro=<MyQDevice.wait_for_state() running at /home/user/usr/local/lib/python3.9/site-packages/pymyq/device.py:123>> closing
completed
@glyph
Copy link
Contributor

glyph commented May 3, 2022

I'm getting the same error when trying to make a little FastAPI service, but a CLI tool running the exact same code works reliably. It seems to me that there is clearly some global state pollution here. I haven't tracked it down yet, but I do find it odd that the internals of pymyq freely intermix calls to async with ClientSession() as websession with just accepting a websession parameter; in the absence of any countervailing explanation my current hypothesis is that somewhere the parameter is accidentally used as the contextmanager instead.

@glyph
Copy link
Contributor

glyph commented May 3, 2022

Here's the problem:

def __init__(self, api: "API", account_json: dict, devices: dict = {}) -> None:

Specifically:

devices: dict = {}

This means you get one dictionary for the entire process, shared between every MyQAccount process.

I'll send a PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants