Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
12f8a35
refactor: improve exception handling and code organization
KJonline May 23, 2026
fc7d0fb
fix: use device_id (not hive_id) to look up data.devices in sensor HI…
KJonline May 23, 2026
82db9cf
fix: guard against None from .get() before .split() in async_init and…
KJonline May 23, 2026
27d4a31
fix: catch ZeroDivisionError in colour-temperature conversion methods
KJonline May 23, 2026
487bc10
fix: epoch_time to_epoch now honours the pattern argument instead of …
KJonline May 23, 2026
d47ee02
fix: remove SSL verify=False and urllib3 warning suppression; use jso…
KJonline May 24, 2026
b7b6494
chore: remove unused POOL, deprecated refresh_tokens and its tests, a…
KJonline May 24, 2026
f6faebc
fix: add device context to bare _LOGGER.error(e) calls in get_mode an…
KJonline May 24, 2026
2461256
fix: updateInterval now sets config.scan_interval instead of silently…
KJonline May 24, 2026
d5154b4
fix: wrap updateInterval new_interval in timedelta(seconds=) to match…
KJonline May 24, 2026
4cd4b6c
fix: guard api_resp_d None before dict access; defend homes list agai…
KJonline May 25, 2026
311a3f3
fix: raise HiveApiError for unhandled Cognito ClientError codes
KJonline May 25, 2026
e564b84
fix: token_created not updated in elif-token branch; bare refreshToke…
KJonline May 25, 2026
4f9486d
fix: close() must not close a caller-provided websession
KJonline May 25, 2026
cd530cd
fix: use safe _get_product_state in get_mode BOOST path (heating + ho…
KJonline May 25, 2026
cffa101
fix: set_boost_off returns False instead of sending mode=None to API
KJonline May 25, 2026
f42095f
fix: return int brightness; return HS 2-tuple from get_color for HA h…
KJonline May 25, 2026
38b710c
fix: raise HiveUnknownConfiguration (not HiveReauthRequired) for empt…
KJonline May 25, 2026
381c78b
fix: device registration wrong exceptions and unnecessary async methods
KJonline May 25, 2026
3f39463
fix: copy schedule slots in get_schedule_nnl to prevent mutation; gua…
KJonline May 25, 2026
706baa5
fix: list→set for battery/mode, remove dead device_attributes code, f…
KJonline May 25, 2026
c269ca0
test: cover remaining branch gaps in session/auth and session/polling
KJonline May 25, 2026
1a0fe4a
refactor: remove deprecated refresh_tokens from HiveApi
KJonline May 25, 2026
5c9830a
test: remove redundant tests from catch-all and relocate unique ones
KJonline May 25, 2026
90c978f
test: migrate all unique tests from catch-all into dedicated files
KJonline May 27, 2026
a773b79
test: remove 3 duplicate tests from module files
KJonline May 27, 2026
f2ebaeb
refactor: eliminate _extended test files by renaming and merging
KJonline May 27, 2026
776a2a3
refactor: consolidate API error handling and remove deprecated code
KJonline Jun 16, 2026
7908dbd
refactor: remove unused _BOOST_MINS constant from test_hotwater.py
KJonline Jun 16, 2026
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
Binary file added .coverage_full
Binary file not shown.
72 changes: 36 additions & 36 deletions .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -268,28 +268,28 @@
"filename": "src/api/hive_auth_async.py",
"hashed_secret": "5dc786e32e3a0a4611daaf397721c6ef64cd71b0",
"is_verified": false,
"line_number": 48
"line_number": 49
},
{
"type": "Secret Keyword",
"filename": "src/api/hive_auth_async.py",
"hashed_secret": "ac9f290e69cee683ba3c63461f1f3fa02765032a",
"is_verified": false,
"line_number": 49
"line_number": 50
},
{
"type": "Secret Keyword",
"filename": "src/api/hive_auth_async.py",
"hashed_secret": "351b174ccf89601f6f4bd3f3970a4aba7d17c98e",
"is_verified": false,
"line_number": 52
"line_number": 53
},
{
"type": "Secret Keyword",
"filename": "src/api/hive_auth_async.py",
"hashed_secret": "576956b5291ac38d04ef5f82cc974286a857f0b2",
"is_verified": false,
"line_number": 109
"line_number": 112
}
],
"src/api/srp_crypto.py": [
Expand All @@ -298,112 +298,112 @@
"filename": "src/api/srp_crypto.py",
"hashed_secret": "3e619ee0820ecf213c2f38c634e416b53defe3b0",
"is_verified": false,
"line_number": 11
"line_number": 10
},
{
"type": "Hex High Entropy String",
"filename": "src/api/srp_crypto.py",
"hashed_secret": "b8e0d506d969f09a9af89ce89fd9759b72c63262",
"is_verified": false,
"line_number": 12
"line_number": 11
},
{
"type": "Hex High Entropy String",
"filename": "src/api/srp_crypto.py",
"hashed_secret": "e97a751edc71e9afbe0c0f63ec94873392833f9f",
"is_verified": false,
"line_number": 13
"line_number": 12
},
{
"type": "Hex High Entropy String",
"filename": "src/api/srp_crypto.py",
"hashed_secret": "92488c021dd524a2f4e116666b3645308fa0e35c",
"is_verified": false,
"line_number": 14
"line_number": 13
},
{
"type": "Hex High Entropy String",
"filename": "src/api/srp_crypto.py",
"hashed_secret": "d4571e2f026f458aecd2950b0eb6aec190276177",
"is_verified": false,
"line_number": 15
"line_number": 14
},
{
"type": "Hex High Entropy String",
"filename": "src/api/srp_crypto.py",
"hashed_secret": "8109d3c2f659f13cb61fc9e71eed574efe8c8fd8",
"is_verified": false,
"line_number": 16
"line_number": 15
},
{
"type": "Hex High Entropy String",
"filename": "src/api/srp_crypto.py",
"hashed_secret": "08cac7461d7b624b88c53ee47da09cbbb84ea290",
"is_verified": false,
"line_number": 17
"line_number": 16
},
{
"type": "Hex High Entropy String",
"filename": "src/api/srp_crypto.py",
"hashed_secret": "95523fea7e6136c6148299dcc3077debfa2976b3",
"is_verified": false,
"line_number": 18
"line_number": 17
},
{
"type": "Hex High Entropy String",
"filename": "src/api/srp_crypto.py",
"hashed_secret": "c978fb77621e86f5e9077653fe5345ac1616b466",
"is_verified": false,
"line_number": 19
"line_number": 18
},
{
"type": "Hex High Entropy String",
"filename": "src/api/srp_crypto.py",
"hashed_secret": "fc02990268ecf8a35a4912d60dab3754e5f43846",
"is_verified": false,
"line_number": 20
"line_number": 19
},
{
"type": "Hex High Entropy String",
"filename": "src/api/srp_crypto.py",
"hashed_secret": "2c2c0ca491a73e95c8965b6641731057b65f6462",
"is_verified": false,
"line_number": 21
"line_number": 20
},
{
"type": "Hex High Entropy String",
"filename": "src/api/srp_crypto.py",
"hashed_secret": "672b25c6be065170206f3fc6346ebb8e84cbb9d3",
"is_verified": false,
"line_number": 22
"line_number": 21
},
{
"type": "Hex High Entropy String",
"filename": "src/api/srp_crypto.py",
"hashed_secret": "99d02e268ea3ee849fb6e359c6c1b019e4d07efd",
"is_verified": false,
"line_number": 23
"line_number": 22
},
{
"type": "Hex High Entropy String",
"filename": "src/api/srp_crypto.py",
"hashed_secret": "e677fc4cb09d99e1e0d30af31f2e209e541e380e",
"is_verified": false,
"line_number": 24
"line_number": 23
},
{
"type": "Hex High Entropy String",
"filename": "src/api/srp_crypto.py",
"hashed_secret": "05b69b06f40cae0c910a15b1ac75b1f7a847eccb",
"is_verified": false,
"line_number": 25
"line_number": 24
},
{
"type": "Hex High Entropy String",
"filename": "src/api/srp_crypto.py",
"hashed_secret": "c7f914bac2d66eb3f8ae3888fa47bf1ada6caaf5",
"is_verified": false,
"line_number": 26
"line_number": 25
}
],
"tests/unit/test_device_registration.py": [
Expand All @@ -419,21 +419,21 @@
"filename": "tests/unit/test_device_registration.py",
"hashed_secret": "d8bce9746547bb7743e5933fbf0fc4f2d2cbcad3",
"is_verified": false,
"line_number": 710
"line_number": 659
},
{
"type": "Secret Keyword",
"filename": "tests/unit/test_device_registration.py",
"hashed_secret": "e4f50034475acff058e17b35679f8ef1e54f86c5",
"is_verified": false,
"line_number": 783
"line_number": 727
},
{
"type": "Secret Keyword",
"filename": "tests/unit/test_device_registration.py",
"hashed_secret": "6ab013c213c685b1f1b1a452796bf22afbd44699",
"is_verified": false,
"line_number": 794
"line_number": 737
}
],
"tests/unit/test_hive_auth.py": [
Expand Down Expand Up @@ -486,14 +486,14 @@
"filename": "tests/unit/test_hive_auth_async.py",
"hashed_secret": "5c5a15a8b0b3e154d77746945e563ba40100681b",
"is_verified": false,
"line_number": 150
"line_number": 173
},
{
"type": "Secret Keyword",
"filename": "tests/unit/test_hive_auth_async.py",
"hashed_secret": "d8bce9746547bb7743e5933fbf0fc4f2d2cbcad3",
"is_verified": false,
"line_number": 206
"line_number": 272
}
],
"tests/unit/test_hive_auth_async_extended.py": [
Expand All @@ -502,49 +502,49 @@
"filename": "tests/unit/test_hive_auth_async_extended.py",
"hashed_secret": "5c5a15a8b0b3e154d77746945e563ba40100681b",
"is_verified": false,
"line_number": 259
"line_number": 260
},
{
"type": "Secret Keyword",
"filename": "tests/unit/test_hive_auth_async_extended.py",
"hashed_secret": "d8bce9746547bb7743e5933fbf0fc4f2d2cbcad3",
"is_verified": false,
"line_number": 340
"line_number": 341
},
{
"type": "Secret Keyword",
"filename": "tests/unit/test_hive_auth_async_extended.py",
"hashed_secret": "76f6b6f16cb41692b330fc806029e8a31e20b69b",
"is_verified": false,
"line_number": 815
"line_number": 813
},
{
"type": "Secret Keyword",
"filename": "tests/unit/test_hive_auth_async_extended.py",
"hashed_secret": "b3ed2cf313e7546085c3c50622143ff31e467d23",
"is_verified": false,
"line_number": 834
"line_number": 832
},
{
"type": "Secret Keyword",
"filename": "tests/unit/test_hive_auth_async_extended.py",
"hashed_secret": "7476b69b5005e05d536361f960a9d18b736dfbfc",
"is_verified": false,
"line_number": 848
"line_number": 846
},
{
"type": "Secret Keyword",
"filename": "tests/unit/test_hive_auth_async_extended.py",
"hashed_secret": "ff9f30d9ba5a4ec386edddeacc27f74ef412085e",
"is_verified": false,
"line_number": 855
"line_number": 853
},
{
"type": "Secret Keyword",
"filename": "tests/unit/test_hive_auth_async_extended.py",
"hashed_secret": "a8ad0732120b9dfed5b99fd6a2aca4fc8ba48d80",
"is_verified": false,
"line_number": 893
"line_number": 890
}
],
"tests/unit/test_hive_helper_extended.py": [
Expand All @@ -553,21 +553,21 @@
"filename": "tests/unit/test_hive_helper_extended.py",
"hashed_secret": "701b389b848a2b1cfab867093101d8d5ac56addd",
"is_verified": false,
"line_number": 134
"line_number": 102
},
{
"type": "Secret Keyword",
"filename": "tests/unit/test_hive_helper_extended.py",
"hashed_secret": "18960546905b75c869e7de63961dc185f9a0a7c9",
"is_verified": false,
"line_number": 141
"line_number": 109
},
{
"type": "Secret Keyword",
"filename": "tests/unit/test_hive_helper_extended.py",
"hashed_secret": "fbf52ca8a72d8ecd77235d3b3e5d014e19ffbff2",
"is_verified": false,
"line_number": 143
"line_number": 111
}
],
"tests/unit/test_session_discovery_extended.py": [
Expand All @@ -580,5 +580,5 @@
}
]
},
"generated_at": "2026-05-17T16:44:49Z"
"generated_at": "2026-06-16T18:53:56Z"
}
3 changes: 3 additions & 0 deletions src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
from .helper.const import SMS_REQUIRED
from .helper.hive_exceptions import (
HiveApiError,
HiveAuthCredentialError,
HiveAuthError,
HiveConfigurationError,
HiveError,
HiveFailedToRefreshTokens,
HiveInvalid2FACode,
HiveInvalidDeviceAuthentication,
Expand Down
27 changes: 11 additions & 16 deletions src/api/device_registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class DeviceRegistrationMixin:
large_a_value: int
client_secret: str | None

async def generate_hash_device(self, device_group_key, device_key):
def generate_hash_device(self, device_group_key, device_key):
"""Generate device hash key."""
# source: https://github.com/amazon-archives/amazon-cognito-identity-js/blob/6b87f1a30a998072b4d98facb49dcaf8780d15b0/src/AuthenticationHelper.js#L137 # pylint: disable=line-too-long

Expand All @@ -81,7 +81,7 @@ async def generate_hash_device(self, device_group_key, device_key):
self.device_password = device_password
return device_secret_verifier_config

async def get_device_authentication_key( # pylint: disable=too-many-positional-arguments
def get_device_authentication_key( # pylint: disable=too-many-positional-arguments
self, device_group_key, device_key, device_password, server_b_value, salt
):
"""Get device authentication key."""
Expand Down Expand Up @@ -120,7 +120,7 @@ async def process_device_challenge(self, challenge_parameters):
"%a %b %d %H:%M:%S UTC %Y"
),
)
hkdf = await self.get_device_authentication_key(
hkdf = self.get_device_authentication_key(
self.device_group_key,
self.device_key,
self.device_password,
Expand Down Expand Up @@ -169,7 +169,7 @@ async def confirm_device(self, device_name: str | None = None):

result = None
try:
device_secret_verifier_config = await self.generate_hash_device(
device_secret_verifier_config = self.generate_hash_device(
self.device_group_key, self.device_key
)
result = await self.loop.run_in_executor(
Expand All @@ -183,14 +183,12 @@ async def confirm_device(self, device_name: str | None = None):
),
)
except botocore.exceptions.ClientError as err:
if err.__class__.__name__ in (
"NotAuthorizedException",
"CodeMismatchException",
):
code = (err.response or {}).get("Error", {}).get("Code", "")
if code == "CodeMismatchException":
raise HiveInvalid2FACode from err
raise HiveApiError from err
except botocore.exceptions.EndpointConnectionError as err:
if err.__class__.__name__ == "EndpointConnectionError":
raise HiveApiError from err
raise HiveApiError from err

return result

Expand All @@ -210,8 +208,7 @@ async def update_device_status(self):
),
)
except botocore.exceptions.EndpointConnectionError as err:
if err.__class__.__name__ == "EndpointConnectionError":
raise HiveApiError from err
raise HiveApiError from err

return result

Expand Down Expand Up @@ -335,10 +332,8 @@ async def forget_device(self, access_token, device_key):
),
)
except botocore.exceptions.ClientError as err:
if err.__class__.__name__ == "NotAuthorizedException":
raise HiveInvalid2FACode from err
raise HiveApiError from err
except botocore.exceptions.EndpointConnectionError as err:
if err.__class__.__name__ == "ResourceNotFoundException":
raise HiveApiError from err
raise HiveApiError from err

return result
Loading
Loading