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

Split login logic #767

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open

Split login logic #767

wants to merge 21 commits into from

Conversation

tetienne
Copy link
Collaborator

@tetienne tetienne commented Feb 14, 2023

Fix #321

The new main entrypoint is now Overkiz.get_client_for.

OverkizClient does not manage anymore the session, it’s up to the calling code to manage it. See my example below.

A StrEnum Server has been created to list all the existing supported servers.

import asyncio
import time

from aiohttp import ClientSession

from pyoverkiz.clients.overkiz import OverkizClient
from pyoverkiz.const import Server
from pyoverkiz.overkiz import Overkiz

USERNAME = ""
PASSWORD = ""


async def main() -> None:

    async with ClientSession() as session:
        client = Overkiz.get_client_for(
            Server.SOMFY_EUROPE, USERNAME, PASSWORD, session
        )
        try:
            await client.login()
        except Exception as exception:  # pylint: disable=broad-except
            print(exception)
            return

        gateways = await client.get_gateways()
        token = await client.generate_local_token(gateways[0].id)
        print(token)
        await client.activate_local_token(gateways[0].id, token, "pyoverkiz")

        local_client: OverkizClient = Overkiz.get_client_for(
            Server.SOMFY_DEV_MODE, gateways[0].id, token, session
        )

        devices = await local_client.get_devices()

        for device in devices:
            print(f"{device.label} ({device.id}) - {device.controllable_name}")
            print(f"{device.widget} - {device.ui_class}")

        await local_client.register_event_listener()

        while True:
            events = await local_client.fetch_events()
            print(events)

            time.sleep(2)


asyncio.run(main())

@tetienne tetienne force-pushed the breaking/rework-login branch 2 times, most recently from 304e2a6 to e3fae0b Compare February 14, 2023 11:46
Copy link
Owner

@iMicknl iMicknl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great already! Would be good to think how this could work for LocalServers as well.

pyoverkiz/servers/overkiz_server.py Outdated Show resolved Hide resolved
@tetienne tetienne force-pushed the breaking/rework-login branch from e3fae0b to 170393b Compare February 14, 2023 14:42
@tetienne tetienne force-pushed the breaking/rework-login branch from f8ad47e to 835e6bb Compare February 14, 2023 21:38
),
"somfy_europe_local": lambda gateway_id, token, session: SomfyLocalClient(
name="Somfy Local(Europe)",
endpoint=f"https://gateway-{gateway_id}.local:8443/enduser-mobile-web/1/enduserAPI/",
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not flexible enough I would say. There will be users (like me) where bonjour is not working (e.g. they have a VPN / different VNET). It would be good if we can specify a full URL, thus an IP or a .local address is both possible.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum, that’s good to now. So on HA side, you have to enter manually the IP of your box?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

pyoverkiz/const.py Outdated Show resolved Hide resolved
@tetienne tetienne marked this pull request as ready for review February 22, 2023 08:12
@tetienne tetienne requested a review from vlebourl as a code owner February 22, 2023 08:12
@tetienne tetienne force-pushed the breaking/rework-login branch from 56be3a5 to 58959d1 Compare February 22, 2023 08:13
Copy link
Owner

@iMicknl iMicknl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had a quick look! Thanks, great improvement.

README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated
@@ -57,13 +63,72 @@ async def main() -> None:
print(f"{device.label} ({device.id}) - {device.controllable_name}")
print(f"{device.widget} - {device.ui_class}")

await client.register_event_listener()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a change compared to our previous behavior? Where we did register this by default during login.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot to clean this from the previous example. There is still no need to do it.


async def _login(self, _username: str, _password: str) -> bool:
"""There is no login for Somfy Local"""
return True
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to raise an exception here.

manufacturer="Somfy",
configuration_url=None,
session=session,
username=domain, # not used
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
username=domain, # not used
username="", # not used

Shall we just pass an empty string? I am still a bit in doubt if we should not make username and password optional, and add token as an optional string.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can check my lastest modifications, username and password are no more within the parent class, but are defined within the specific implementation.


class Overkiz:
@staticmethod
def get_client_for(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we call it get_client_for or create_client. With every call, we create a new client, we don't reuse the existing client in Python like this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that’s a static method which create a new client each time. I will rename it if it confused you.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Co-authored-by: Mick Vleeshouwer <[email protected]>
@tetienne tetienne force-pushed the breaking/rework-login branch from b4971f3 to cdafe27 Compare March 2, 2023 08:25
README.md Outdated
@@ -57,13 +63,72 @@ async def main() -> None:
print(f"{device.label} ({device.id}) - {device.controllable_name}")
print(f"{device.widget} - {device.ui_class}")

await client.register_event_listener()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot to clean this from the previous example. There is still no need to do it.

README.md Outdated
asyncio.run(main())
```

### Cloud API
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### Cloud API
### Local API


class Overkiz:
@staticmethod
def get_client_for(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that’s a static method which create a new client each time. I will rename it if it confused you.

manufacturer="Somfy",
configuration_url=None,
session=session,
username=domain, # not used
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can check my lastest modifications, username and password are no more within the parent class, but are defined within the specific implementation.


class Overkiz:
@staticmethod
def get_client_for(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

_ssl: bool = field(default=False, init=False)
token: str = field(repr=lambda _: "***")

async def _login(self) -> bool:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can we verify if the local login works?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can call any command to see if it works.
But I don’t think we should put a check here. There is no login endpoint for the local api, and so we should not call it.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's see how it looks when we have a basic implementation using this branch in core :)

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

Successfully merging this pull request may close these issues.

Implement auth factory
2 participants