Skip to content

fix: 8 bug fixes — sensor data loss, SSL bypass, JSON injection, ZeroDivisionError, and more - #145

Merged
KJonline merged 29 commits into
devfrom
bug-analysis
Jun 20, 2026
Merged

fix: 8 bug fixes — sensor data loss, SSL bypass, JSON injection, ZeroDivisionError, and more#145
KJonline merged 29 commits into
devfrom
bug-analysis

Conversation

@KJonline

Copy link
Copy Markdown
Contributor

Summary

  • Sensor data loss (sensor.py:160): HIVE_TYPES["Sensor"] branch was keying into data.devices with hive_id instead of device_id, causing device_data, props, and parent_device to always be empty for contact/motion sensors
  • Security (hive_async_api.py): Removed verify=False SSL bypass and urllib3.disable_warnings; replaced string-concatenated JSON payload in set_state with json.dumps to eliminate injection risk
  • Crash on zero (color.py): Colour-temperature conversion methods silently swallowed KeyError but not ZeroDivisionError — added ZeroDivisionError to all three except clauses
  • None dereference (hive_auth_async.py): async_init and get_password_authentication_key called .split() on values from .get() without guarding against None; now raises HiveUnknownConfiguration with a descriptive message
  • Broken epoch_time (hive_helper.py): The to_epoch branch hardcoded its own pattern, ignoring the pattern argument passed by the caller
  • updateInterval no-op (compat_aliases.py): The HA backwards-compat alias was silently returning True without updating config.scan_interval; now correctly sets timedelta(seconds=new_interval)
  • Dead code removed (srp_crypto.py, hive_async_api.py): Unused ThreadPoolExecutor pool (spawned threads on import), deprecated refresh_tokens method and its tests, dead if url is not None guard
  • Log context (heating.py, hotwater.py, boost.py): Bare _LOGGER.error(e) calls now include method name and device name

Test Plan

  • pytest tests/ — 956 passed, 99.17% coverage (above 99% threshold)
  • One pre-existing failure in test_polling.py::TestGetDevicesSlowPoll::test_auth_error_sets_last_poll_slow_false — predates this branch, unrelated to these changes
  • New unit tests added for every fix (sensor device_id key, None guards, ZeroDivisionError paths, epoch_time pattern, json.dumps encoding, updateInterval timedelta)

🤖 Generated with Claude Code

KJonlineand others added 27 commits May 23, 2026 16:37
- 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>
Comment threadtests/unit/test_hotwater.py Fixed
@codecov

codecovBot commented Jun 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.96%. Comparing base (3838609) to head (7908dbd).

Additional details and impacted files

Impacted file tree graph

@@ 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 
FlagCoverage Δ
unittests99.96% <100.00%> (-0.01%)⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing linesCoverage Δ
src/__init__.py100.00% <ø> (ø)
src/api/device_registration.py100.00% <100.00%> (ø)
src/api/hive_api.py100.00% <100.00%> (ø)
src/api/hive_async_api.py100.00% <100.00%> (ø)
src/api/hive_auth_async.py100.00% <100.00%> (ø)
src/api/srp_crypto.py100.00% <ø> (ø)
src/devices/boost.py100.00% <100.00%> (ø)
src/devices/color.py100.00% <100.00%> (ø)
src/devices/heating.py100.00% <100.00%> (ø)
src/devices/hotwater.py100.00% <100.00%> (ø)
... and 13 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

- 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
Comment threadtests/unit/test_hotwater.py Fixed
@KJonline
KJonline merged commit e3e70d0 into devJun 20, 2026
10 checks passed
@KJonline
KJonline deleted the bug-analysis branch June 20, 2026 08:59
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@KJonline