fix: 8 bug fixes — sensor data loss, SSL bypass, JSON injection, ZeroDivisionError, and more#145
Merged
Conversation
- Replace __class__.__name__ string checks with direct error.response parsing for boto3 ClientError - Remove redundant if checks when re-raising EndpointConnectionError as HiveApiError - Defer asyncio.get_event_loop() call to async_init() using get_running_loop() - Remove deprecated pool_region parameter from HiveAuthAsync.__init__ - Add HiveError base class and reorganize exception hierarchy (HiveConfigurationError, HiveAuthCredentialError)
…VE_TYPES branch Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… get_password_authentication_key Import HiveUnknownConfiguration and raise it instead of letting AttributeError propagate when REGION or UPID are absent from the SSO login info response, and when _pool_id is None or missing an underscore in get_password_authentication_key. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extend the except clause in get_min_color_temp, get_max_color_temp, and get_color_temp from KeyError-only to (KeyError, ZeroDivisionError) so that a zero colourTemperature value returned by the Hive API returns None instead of raising an unhandled ZeroDivisionError. Tests added for all three cases. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…hardcoding it Removes the line that overwrote the caller-supplied `pattern` with a hardcoded Hive format string, so custom format strings are respected. Adds TestEpochTimePattern tests to confirm the fix and prevent regression. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n.dumps in set_state - Replace manual string-concatenation JSON building in set_state with json.dumps(kwargs) to prevent JSON injection when kwarg values contain double-quotes or backslashes - Remove requests.get(verify=False) SSL bypass from get_login_info - Remove urllib3 import and disable_warnings call that suppressed the SSL warning - Update TestGetLoginInfo assertion to match new call signature (no verify=False) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nd dead url/status guard Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… returning True Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… SessionConfig type Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nst null Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Unhandled ClientError codes in initiate_auth and respond_to_auth_challenge were silently swallowed, leaving response=None and crashing later with TypeError. async_init would AttributeError on None when get_login_info returned no data. All three now raise HiveApiError/HiveUnknownConfiguration. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n access update_tokens() elif branch never set token_created (leaving it at datetime.min) and crashed on KeyError when refreshToken was absent. hive_refresh_tokens() used bare dict access for refreshToken. Both now use safe access patterns and update token_created consistently. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
close() previously closed the websession unconditionally. Added _owns_websession flag (True when websession=None at construction time) so callers who inject their own session retain ownership and lifecycle control. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…twater) Bare dict access data["props"]["previous"]["mode"] raised KeyError when previous was absent, silently swallowed by the outer except and logged as an error. Replaced with _get_product_state which returns None safely without triggering the error log path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When props.previous.mode is absent, set_boost_off was calling _execute_state_change(mode=None) causing a silent API failure. Now returns False early with a warning when previous mode cannot be determined. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…s_color Brightness calculation returned a float (127.5 for 50%), but HA light entities require int. get_color returned an RGB 3-tuple (r,g,b) but HA hs_color expects (hue_degrees, saturation_percent) — Hive API stores these natively so no conversion is needed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…y device data Empty device/product data after polling means configuration is unknown, not that tokens expired — using HiveReauthRequired wrongly triggered a full re-login. Also fixed bare d["id"]/p["id"] access in create_devices to use .get() with a fallback key to avoid KeyError when the id field is absent. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- confirm_device: non-CodeMismatch ClientErrors now raise HiveApiError instead of being silently swallowed - forget_device: NotAuthorizedException now raises HiveApiError instead of the misleading HiveInvalid2FACode; all ClientErrors propagate - generate_hash_device and get_device_authentication_key are pure computation with no I/O — converted from async def to def; callers updated to remove await Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rd hotwater schedule NNL - get_schedule_nnl: slot dicts are now shallow-copied before Start_DateTime is added, preventing in-place mutation of the original schedule data that caused stale results on repeated calls - hotwater.get_state: guard snan["now"] access so an empty NNL (fewer than 3 schedule slots) does not raise KeyError; state falls back to the base status value instead Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ix Map.__delattr__ - SessionConfig.battery and .mode changed from list to set; discovery.py callers updated from .append() to .add() for O(1) membership checks - HiveAttributes.get_mode: removed no-op HIVETOHA["Attribute"] lookup — mode strings never matched boolean keys so the lookup always passed through; now returns raw mode value directly - HiveAttributes.get_battery: removed dead error_check call — battery state is always an integer, never False or "Failed", so error_check never triggered a meaningful action - Map.__delattr__: was raising KeyError for missing keys via dict.__delitem__; now wraps in AttributeError to match Python's attribute protocol Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- update_tokens elif-'token' branch with update_expiry_time=False: token_created must not be updated (106->109 branch was uncovered) - get_devices with homes_data as a non-dict (list): must not crash and home_id stays unset (196->181 branch was uncovered) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The method was never called by any production or test code and was explicitly marked as superseded by AWS token management. Removing it brings coverage to 100%. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Deleted ~20 tests from test_remaining_branches.py that were exact duplicates of tests in their dedicated _extended files. Moved the two genuinely unique tests (get_state exception handler and dict-under- sensitive-key sanitize path) to test_heating_extended.py and test_helpers.py respectively. Coverage remains at 100%. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Moves all remaining unique test classes from test_remaining_branches.py into their respective device/session/helper extended files. Deletes the empty catch-all. Coverage remains at 100%. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
test_schedule_mode_returns_nnl, test_motionsensor_returns_motion_status, and test_empty_devices_after_get_devices_raises_unknown_configuration were identical (same mocked setup, same assertions) to their counterparts in the corresponding unit/_extended files. Removed from module files; unit versions retained. Coverage remains at 100%. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Renamed 9 standalone extended files (git mv) and merged 4 extended files into their base counterparts, deduplicating helpers and consolidating all test classes under clean names with no _extended suffix. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## dev #145 +/- ##
==========================================
- Coverage 99.96% 99.96% -0.01%
==========================================
Files 30 30
Lines 2594 2508 -86
Branches 293 292 -1
==========================================
- Hits 2593 2507 -86
Partials 1 1
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
- Extracted _call_endpoint helper in both sync/async API classes to eliminate repetitive try/except blocks across get_all, get_devices, get_products, get_actions, motion_sensor, get_weather, set_state, and set_action - Removed get_login_info from HiveApiAsync (unused; sync version retained) - Replaced manual JSON string building in set_state with json.dumps - Replaced fragile string manipulation for SSO parsing with regex in
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
sensor.py:160):HIVE_TYPES["Sensor"]branch was keying intodata.deviceswithhive_idinstead ofdevice_id, causingdevice_data,props, andparent_deviceto always be empty for contact/motion sensorshive_async_api.py): Removedverify=FalseSSL bypass andurllib3.disable_warnings; replaced string-concatenated JSON payload inset_statewithjson.dumpsto eliminate injection riskcolor.py): Colour-temperature conversion methods silently swallowedKeyErrorbut notZeroDivisionError— addedZeroDivisionErrorto all threeexceptclauseshive_auth_async.py):async_initandget_password_authentication_keycalled.split()on values from.get()without guarding againstNone; now raisesHiveUnknownConfigurationwith a descriptive messageepoch_time(hive_helper.py): Theto_epochbranch hardcoded its own pattern, ignoring thepatternargument passed by the callerupdateIntervalno-op (compat_aliases.py): The HA backwards-compat alias was silently returningTruewithout updatingconfig.scan_interval; now correctly setstimedelta(seconds=new_interval)srp_crypto.py,hive_async_api.py): UnusedThreadPoolExecutorpool (spawned threads on import), deprecatedrefresh_tokensmethod and its tests, deadif url is not Noneguardheating.py,hotwater.py,boost.py): Bare_LOGGER.error(e)calls now include method name and device nameTest Plan
pytest tests/— 956 passed, 99.17% coverage (above 99% threshold)test_polling.py::TestGetDevicesSlowPoll::test_auth_error_sets_last_poll_slow_false— predates this branch, unrelated to these changes🤖 Generated with Claude Code