Skip to content

Release v2.4.0 - #5

Merged
telard-pixel merged 1 commit into
mainfrom
dev
Jun 13, 2026
Merged

Release v2.4.0#5
telard-pixel merged 1 commit into
mainfrom
dev

Conversation

@telard-pixel

@telard-pixel telard-pixel commented Jun 13, 2026

Copy link
Copy Markdown
Collaborator

Automated release PR for v2.4.0.

Merge this PR with squash only. The post-merge workflow will move dev to the squash commit, recreate v2.4.0 on that commit, and publish the release.

Summary by CodeRabbit

  • Chores
    • Released version 2.4.0 of the integration.

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7d928e0e-9065-47c0-871f-ecc7518990ee

📥 Commits

Reviewing files that changed from the base of the PR and between 8a4c846 and 2fc65a9.

📒 Files selected for processing (1)
  • custom_components/haier_hon/manifest.json

📝 Walkthrough

Walkthrough

The haier_hon integration manifest is updated to release version 2.4.0 and reformatted to use multi-line layouts for codeowners and requirements fields. The dependency version and owner identifier remain unchanged.

Changes

Manifest Configuration

Layer / File(s) Summary
Version bump and field reformatting
custom_components/haier_hon/manifest.json
Integration version advances from 2.3-rc1 to 2.4.0, with codeowners and requirements reformatted from single-line to multi-line array and object syntax while preserving @telard-pixel owner and pyhon==0.17.5 dependency.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~2 minutes

Possibly related PRs

Poem

A manifest takes flight,
Version bumps glow bright,
Two-four-oh declared with care,
Fields laid out so fair,
Deps and owners rest at ease,
Config formatted to please. 🐰✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Release v2.4.0' accurately reflects the main change: updating the manifest version from 2.3-rc1 to 2.4.0 in the haier_hon integration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Comment @coderabbitai help to get the list of available commands and usage tips.

@telard-pixel
telard-pixel merged commit 13da89f into main Jun 13, 2026
4 checks passed
This was referenced Jun 13, 2026
tis24dev added a commit that referenced this pull request Jun 18, 2026
Second native-transport piece: parse_appliance_list rewrites pyhon's inline
extraction in api.load_appliances (the v2.7.1 unified-api fix). Defensive
iterative walk modules->applianceList->payload->appliances; any unexpected shape
-> [] (so the caller treats schema drift as '0 appliances').

DELIBERATE hardening vs pyhon (loop point #5, not freezing pyhon's fragility):
pyhon's chained .get() raises AttributeError when an intermediate level is
non-dict (e.g. {'modules': None}); ours returns []. Differential test: oracle is
a verbatim transcription of pyhon's parsing (inline in an async+HTTP method, not
importable); _EQUAL fixtures must match pyhon, _HARDENED fixtures are where pyhon
raises and ours fails safe. Refuter: exhaustive 84-input sweep, no silent
divergence (the only constructible one needs a non-dict Mapping, unreachable from
resp.json()). 125 passed, 1 skipped.
tis24dev added a commit that referenced this pull request Jun 18, 2026
…p model)

Per the app model (apk/analysis/per-type-derivations.md #5): connectivity is
first-class and the app does not fake-zero values when a device is offline. Replace
the per-type offline param-zeroing with HA-idiomatic availability.

- Remove the offline zeroing from the per-type extras (td/wd/dw/ov/wm) and the now-dead
  `_set` helper from appliances/base.py. Offline devices keep their last-known values
  in the data (no fake 0/off).
- base_entity.available now also returns False when the device is disconnected
  (the `available` attribute, set by the engine from lastConnEvent.category and exposed
  universally on the ROOT). So an offline device's entities go "unavailable" (idiomatic
  HA) rather than showing stale/zeroed values; defaults True when the flag is absent
  (a device that errored before load isn't wrongly hidden).

Chain verified end-to-end (engine available -> hon_client._get_attributes ->
coordinator.data[id]['attributes']['available'] -> base_entity.available). All 8 entity
classes inherit HonBaseEntity, so the gate applies consistently; online behavior is
unchanged (zeroing only ran offline).

Tests: test_no_offline_zeroing + test_missing_machmode_no_crash (engine), connected/
disconnected/absent-flag gating (test_entity_availability); engine_appliances golden
regenerated (offline entries now keep last-known values, not 0). Adversarial pool
(2 refuters): chain correctness HOLDS, regression/scope HOLDS (only engine_appliances
golden changed, mutations guard both the no-zeroing and the availability gate).
260 passed, 1 skipped.

Note: the app keeps last values visible + a disconnected badge; we chose the HA-idiomatic
"unavailable". A future always-available connectivity binary_sensor could add a positive
indicator.
tis24dev added a commit that referenced this pull request Jun 23, 2026
…lf-degrees

Audit finding #5. The climate entity hardcoded min/max/step (16/30/1.0) and
sent the setpoint via str(int(temp)), silently dropping the half degree (the
same bug number.py had already fixed for its setpoints).

Read (min, max, step) from the device's real tempSel parameter via param_range
(min_temp/max_temp/target_temperature_step are now dynamic properties, falling
back to 16-30/1.0 when the device does not expose a range), so the UI only
offers values the device accepts. Serialize the setpoint as a clean int string
for integers and keep the decimals otherwise, letting the engine Range setter
validate against the real step instead of truncating.

Tests: cover the device-derived range, the fallback, and integer/fractional
serialization.
tis24dev added a commit that referenced this pull request Jun 23, 2026
Adversarial-refuter follow-up on the climate-AC fixes (#5/#9/#16):
- async_turn_on failure path now asserts it raises command_error (a swallow
  mutant previously survived: a failed send would be silently ignored).
- async_turn_on with client None raises appliance_or_client_unavailable.
- _derive_hvac_modes skips machMode enum codes not in AC_MODE_MAP (real devices
  may report unmapped codes) instead of crashing the entity.
tis24dev added a commit that referenced this pull request Jun 23, 2026
…lf-degrees

Audit finding #5. The climate entity hardcoded min/max/step (16/30/1.0) and
sent the setpoint via str(int(temp)), silently dropping the half degree (the
same bug number.py had already fixed for its setpoints).

Read (min, max, step) from the device's real tempSel parameter via param_range
(min_temp/max_temp/target_temperature_step are now dynamic properties, falling
back to 16-30/1.0 when the device does not expose a range), so the UI only
offers values the device accepts. Serialize the setpoint as a clean int string
for integers and keep the decimals otherwise, letting the engine Range setter
validate against the real step instead of truncating.

Tests: cover the device-derived range, the fallback, and integer/fractional
serialization.
tis24dev added a commit that referenced this pull request Jun 23, 2026
Adversarial-refuter follow-up on the climate-AC fixes (#5/#9/#16):
- async_turn_on failure path now asserts it raises command_error (a swallow
  mutant previously survived: a failed send would be silently ignored).
- async_turn_on with client None raises appliance_or_client_unavailable.
- _derive_hvac_modes skips machMode enum codes not in AC_MODE_MAP (real devices
  may report unmapped codes) instead of crashing the entity.
tis24dev added a commit that referenced this pull request Jun 23, 2026
…lf-degrees

Audit finding #5. The climate entity hardcoded min/max/step (16/30/1.0) and
sent the setpoint via str(int(temp)), silently dropping the half degree (the
same bug number.py had already fixed for its setpoints).

Read (min, max, step) from the device's real tempSel parameter via param_range
(min_temp/max_temp/target_temperature_step are now dynamic properties, falling
back to 16-30/1.0 when the device does not expose a range), so the UI only
offers values the device accepts. Serialize the setpoint as a clean int string
for integers and keep the decimals otherwise, letting the engine Range setter
validate against the real step instead of truncating.

Tests: cover the device-derived range, the fallback, and integer/fractional
serialization.
tis24dev added a commit that referenced this pull request Jun 23, 2026
Adversarial-refuter follow-up on the climate-AC fixes (#5/#9/#16):
- async_turn_on failure path now asserts it raises command_error (a swallow
  mutant previously survived: a failed send would be silently ignored).
- async_turn_on with client None raises appliance_or_client_unavailable.
- _derive_hvac_modes skips machMode enum codes not in AC_MODE_MAP (real devices
  may report unmapped codes) instead of crashing the entity.
tis24dev added a commit that referenced this pull request Jun 25, 2026
#2, #5)

- _persist_refresh_token: update the config entry only when the live refresh
  token actually rotated (at setup and on each coordinator poll), so a rotated
  token survives a restart instead of replaying a stale one (#2).
- HonClient: re-seed the stored refresh token after setup, after MFA verify, and
  after each poll, so a mid-life rotation is not lost on the next refresh (#1, #5).
- tests: change-guarded persist (AST-checked call sites), mid-life rotation
  seed-sync, and the reauth re-seed handoff.
tis24dev added a commit that referenced this pull request Jun 28, 2026
…ges, #7 label)

- CodeRabbit #5: add the empty-string dryLevel sentinel to DRY_LEVEL_SENTINELS
  ('' / '0' / '11' are all "no dry level" per the app's hasDryLevelValue), so a blank
  code is dropped from the select options and does not count toward the gate.
- CodeRabbit #6: the range materializer no longer overshoots the max. option_choices
  used `current <= hi + step/2`, which could emit a value beyond the declared max on a
  step that overshoots (0..10 step 20 -> ["0","20"]); it now uses a tight `+1e-9` bound
  (float-drift only). is_settable_option for a drop-free range now requires
  `lo + step <= hi` (>= 2 reachable values) instead of `max > min`, so a single-real-
  value range is not offered as a control. (A defensive step<=0 guard is added too;
  param_range already coerces 0->1 and rejects negatives, so it is unreachable in
  practice.) No real Haier range is affected (all integer, on-grid max).
- CodeRabbit #7: give anti_crease_time a label distinct from anticrease
  (en "Anti-crease time", it "Tempo antipiega") so a WD exposing both does not show two
  identical switches.

Tests added (mutation-proven): uneven-range no-overshoot + gate, '' sentinel drop,
anti_crease_time/anticrease label distinctness. 3-lens refuter pool: HOLD. 907 passed,
1 skipped (local + CI-clean).
Sign up for free to 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