From 2694a73e68644ee8a053a696ce978963efc98668 Mon Sep 17 00:00:00 2001 From: Matt Dennis Date: Tue, 20 Oct 2026 14:56:38 +0100 Subject: [PATCH] fix: parse single-quoted SSO config values on the Hive login page Since 2026-10-20 https://sso.hivehome.com/ emits its bootstrap config as window.HiveSSOPoolId = 'eu-west-1_...' (single quotes, spaces around =) instead of window.HiveSSOPoolId="...". _SSO_ASSIGNMENT only matched double quotes, so get_login_info() raised KeyError, returned None, and HiveAuthAsync.async_init crashed with AttributeError on self.data.get. Home Assistant then fails to set up the Hive integration after a restart (home-assistant/core#187516). Accept either quote character, requiring the closing quote to match, and add unit tests for the new format and for mismatched quotes. Co-Authored-By: Claude Fable 5.1 --- src/api/hive_api.py | 10 ++++++++-- tests/unit/test_hive_api.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/api/hive_api.py b/src/api/hive_api.py index 0bf1ba76..e88aba07 100644 --- a/src/api/hive_api.py +++ b/src/api/hive_api.py @@ -18,7 +18,10 @@ # json.JSONDecodeError subclass. _REQUEST_ERRORS = (OSError, RuntimeError, json.JSONDecodeError) -_SSO_ASSIGNMENT = re.compile(r'window\.(\w+)\s*=\s*"([^"]*)"') +# The SSO bootstrap page embeds its config as `window. = ` +# assignments. The value quoting changed from double to single quotes on +# 2026-10-20, so accept either (and require the closing quote to match). +_SSO_ASSIGNMENT = re.compile(r"""window\.(\w+)\s*=\s*(["'])(.*?)\2""") class HiveApi: @@ -118,7 +121,10 @@ def get_login_info(self): "get_login_info - Login info response status: %s", data.status_code ) script_text = PyQuery(data.content)("script:first").text() - sso_values = dict(_SSO_ASSIGNMENT.findall(script_text)) + sso_values = { + key: value + for key, _quote, value in _SSO_ASSIGNMENT.findall(script_text) + } login_data = { "UPID": sso_values["HiveSSOPoolId"], diff --git a/tests/unit/test_hive_api.py b/tests/unit/test_hive_api.py index 4511c104..debba3a5 100644 --- a/tests/unit/test_hive_api.py +++ b/tests/unit/test_hive_api.py @@ -206,6 +206,43 @@ def test_successful_parse_returns_login_data(self): # REGION mirrors UPID assert result["REGION"] == "eu-west-1_abc" + def test_single_quoted_values_are_parsed(self): + """Since 2026-10-20 the SSO page uses single quotes and spaces around =.""" + api = _make_api() + html_content = ( + b"" + ) + mock_resp = MagicMock() + mock_resp.content = html_content + mock_resp.status_code = 200 + + with patch("apyhiveapi.api.hive_api.requests.get", return_value=mock_resp): + result = api.get_login_info() + + assert result == { + "UPID": "eu-west-1_abc", + "CLIID": "client123", + "REGION": "eu-west-1_abc", + } + + def test_mismatched_quotes_are_not_parsed(self): + """A value must be closed by the same quote character it was opened with.""" + api = _make_api() + html_content = b"" + mock_resp = MagicMock() + mock_resp.content = html_content + mock_resp.status_code = 200 + + with patch("apyhiveapi.api.hive_api.requests.get", return_value=mock_resp): + result = api.get_login_info() + + assert result is None + def test_os_error_calls_error_and_raises_connection_error(self): api = _make_api() with (