Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions src/elmo/api/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@

from .. import query as q
from ..__about__ import __version__
from ..utils import _camel_to_snake_case, _sanitize_session_id
from ..systems import ELMO_E_CONNECT
from ..utils import (
_camel_to_snake_case,
_sanitize_session_id,
extract_session_id_from_html,
)
from .decorators import require_lock, require_session
from .exceptions import (
CodeError,
Expand Down Expand Up @@ -49,14 +54,15 @@ def __init__(self, base_url=None, domain=None, session_id=None):
self._session_id = session_id
self._panel = None
self._lock = Lock()

# Debug
_LOGGER.debug(f"Client | Library version: {__version__}")
_LOGGER.debug(f"Client | Router: {self._router._base_url}")
_LOGGER.debug(f"Client | Domain: {self._domain}")

def auth(self, username, password):
"""Authenticate the client and retrieves the access token. This method uses
the Authentication API.
the Authentication API, or the web login form if the base_url is Elmo E-Connect.

Args:
username: the Username used for the authentication.
Expand All @@ -69,11 +75,28 @@ def auth(self, username, password):
the `ElmoClient` instance.
"""
try:
if self._router._base_url == ELMO_E_CONNECT:
# Web login is required for Elmo E-Connect because, at the moment, the
# e-Connect Cloud API login does not register the client session in the backend.
# This prevents the client from attaching to server events (e.g. long polling updates).
web_login_url = f"https://webservice.elmospa.com/{self._domain}"
payload = {
"IsDisableAccountCreation": "True",
"IsAllowThemeChange": "True",
"UserName": username,
"Password": password,
"RememberMe": "false",
}
_LOGGER.debug("Client | e-Connect Web Login detected")
web_response = self._session.post(web_login_url, data=payload)
web_response.raise_for_status()

# API login
payload = {"username": username, "password": password}
if self._domain is not None:
payload["domain"] = self._domain

_LOGGER.debug("Client | Client Authentication")
_LOGGER.debug("Client | API Authentication")
response = self._session.get(self._router.auth, params=payload)
response.raise_for_status()
except HTTPError as err:
Expand All @@ -84,8 +107,11 @@ def auth(self, username, password):

# Store the session_id and the panel details (if available)
data = response.json()
self._session_id = data["SessionId"]
self._panel = {_camel_to_snake_case(k): v for k, v in data.get("Panel", {}).items()}
if self._router._base_url == ELMO_E_CONNECT:
self._session_id = extract_session_id_from_html(web_response.text)
else:
self._session_id = data["SessionId"]

# Register the redirect URL and try the authentication again
if data["Redirect"]:
Expand Down
28 changes: 28 additions & 0 deletions src/elmo/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import re
from functools import lru_cache

from .api.exceptions import ParseError

_LOGGER = logging.getLogger(__name__)


Expand Down Expand Up @@ -58,3 +60,29 @@ def _camel_to_snake_case(name):
name = name.lower()

return name


def extract_session_id_from_html(html_content: str) -> str:
"""Extract the session ID from the HTML source containing the specific JavaScript block.

This function uses a raw string (r"") for the regex pattern to avoid escaping issues.
The regex pattern is designed to find "var sessionId = '...'" and capture the ID within the quotes.
It captures any character except the closing single quote.

Args:
html_content (str): The HTML source code as a string.

Returns:
str: The extracted session ID string.

Raises:
ParseError: If the session ID is not found in the HTML content.
"""
pattern = r"var\s+sessionId\s*=\s*'([^']+)'"
match = re.search(pattern, html_content)
if match:
return match.group(1)
else:
_LOGGER.error("Client | Session ID not found in e-Connect status page.")
_LOGGER.debug("Client | HTML content: %s", html_content)
raise ParseError("Session ID not found in e-Connect status page.")
Loading