Skip to content

Release v2.4.3 - #8

Merged
telard-pixel merged 3 commits into
mainfrom
dev
Jun 13, 2026
Merged

Release v2.4.3#8
telard-pixel merged 3 commits 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.3.

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

Summary by CodeRabbit

  • New Features

    • Haier HON integration now includes device consumption statistics in appliance attributes, with real-time values taking precedence over historical data
  • Chores

    • Version updated to 2.4.3
    • Enhanced release workflow validation and documentation

tis24dev and others added 3 commits June 13, 2026 16:08
The release pipeline pinned everything to a frozen release-source-sha captured
at tag time, which broke as soon as dev advanced during an open release PR (e.g.
pushing CodeRabbit fix commits):

- release-guard required release-source-sha == PR head and fetched the exact
  tag, so a fix commit failed it with "dev advanced..." or, when a concurrent
  intake/post-merge run was mid delete+recreate of the tag, with the cryptic
  "fatal: couldn't find remote ref refs/tags/<tag>" (exit 128).
- post-merge refused to sync dev when origin/dev != the frozen source-sha,
  leaving dev behind main after a release (manifest stuck on the old version).

Switch both to validating the LIVE state instead of a frozen commit:

- release-guard: drop the source-sha pin and the tag fetch; just require the
  manifest version on origin/dev to match the release tag. Also allow
  marker-less maintenance dev -> main PRs through (post-merge already handles
  the no-release case).
- post-merge: gate the dev sync on tree equality (merge tree == origin/dev tree)
  instead of sha equality, and lease on the freshly observed dev. This tolerates
  review fix commits (which rewrite history) while still refusing to clobber dev
  if it carries content the squash does not include. The published tag is
  created on the squash commit at merge time, as before.

Document the new "push review fixes straight to dev" flow.
The update loop already calls load_statistics(), but _get_attributes() only
merged appliance.attributes (+parameters) and appliance.settings, never
appliance.statistics. The lifetime/cycle consumption counters
(totalElectricityUsed, totalWaterUsed, totalWashCycle, currentElectricityUsed,
currentWaterUsed) live in the statistics container, so the consumption sensors
read None and showed as unknown.

Merge appliance.statistics first (lowest precedence) so real-time attributes and
settings still win on any key collision. Add tests covering the merge, the
conflict precedence, and the missing-statistics case.
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5b63b4ef-e426-493f-9613-eeab9f430898

📥 Commits

Reviewing files that changed from the base of the PR and between 43eb5f3 and c799f67.

📒 Files selected for processing (6)
  • .github/workflows/post-merge-release.yml
  • .github/workflows/release-guard.yml
  • custom_components/haier_hon/hon_client.py
  • custom_components/haier_hon/manifest.json
  • docs/release-workflow.md
  • tests/test_program_select.py

📝 Walkthrough

Walkthrough

This PR adds appliance statistics extraction to the Home Assistant component's attribute gathering, introduces corresponding test coverage, and refactors release workflow automation to validate against live dev-branch state with stricter gates and reordered release checks, including updated documentation.

Changes

Statistics Feature Enhancement

Layer / File(s) Summary
Statistics extraction and attribute merging
custom_components/haier_hon/hon_client.py, tests/test_program_select.py
_get_attributes() now reads and merges appliance.statistics before attributes and settings, allowing later values to override conflicting keys. Tests verify statistics are merged, real-time attributes override statistics on conflict, and missing statistics is handled without errors.
Component version increment
custom_components/haier_hon/manifest.json
Version bumped from 2.4.2 to 2.4.3.

Release Workflow Refinement

Layer / File(s) Summary
Release guard validation
.github/workflows/release-guard.yml
Distinguishes maintenance PRs (no release-tag marker) from release PRs; exits successfully for maintenance. For release PRs, validates tag and fetches live dev branch state (via DEV_SHA from origin/dev), asserting the manifest on dev matches the release tag. Removes prior release-source-sha marker requirement and tag/SHA consistency checks.
Post-merge release finalization
.github/workflows/post-merge-release.yml
Replaces squash-diff verification with a content gate comparing merged commit tree against live origin/dev; uses DEV_SHA for dev fast-forward push. Reorders the release-exists check to before retagging, then force-updates and pushes the tag to the merge commit before creating the GitHub release.
Release workflow documentation
docs/release-workflow.md
Clarifies that review fixes are pushed to dev while release-guard revalidates against live dev state. Restates squash-only merge requirement and describes post-merge automation: moving dev to squash, creating the tag, and publishing the release. Notes that final tag is created at merge time with no re-tagging needed after review fixes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • telard-pixel/haier_hon#2: Both PRs modify release automation workflows (post-merge-release.yml and release-guard.yml) with changes to tag validation, marker handling, and branch synchronization logic.

Poem

🐰 Statistics now flow like morning dew,
Attributes merge in precedent queue,
Release gates guard with a live dev's sight,
Tagging reordered, automation tight,
A bumped little version hops into view! 🎉

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 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 fbcd5f6 into main Jun 13, 2026
2 of 3 checks passed
This was referenced Jun 13, 2026
tis24dev added a commit that referenced this pull request Jun 23, 2026
Audit finding #8. The 11 await resp.json() call-sites in api.py omitted
content_type=None, while connection._intercept and auth.py use it deliberately
because the hOn/Salesforce cloud sometimes returns valid JSON with a non-JSON
Content-Type. A response that passes _intercept (content_type=None) could then
raise aiohttp.ContentTypeError in api.py, crashing load_*/send_command.

Align all call-sites to resp.json(content_type=None).

Test: a strict response that rejects json() without content_type=None, exercised
through load_appliances and load_commands.
tis24dev added a commit that referenced this pull request Jun 23, 2026
Audit finding #8. The 11 await resp.json() call-sites in api.py omitted
content_type=None, while connection._intercept and auth.py use it deliberately
because the hOn/Salesforce cloud sometimes returns valid JSON with a non-JSON
Content-Type. A response that passes _intercept (content_type=None) could then
raise aiohttp.ContentTypeError in api.py, crashing load_*/send_command.

Align all call-sites to resp.json(content_type=None).

Test: a strict response that rejects json() without content_type=None, exercised
through load_appliances and load_commands.
tis24dev added a commit that referenced this pull request Jun 23, 2026
Audit finding #8. The 11 await resp.json() call-sites in api.py omitted
content_type=None, while connection._intercept and auth.py use it deliberately
because the hOn/Salesforce cloud sometimes returns valid JSON with a non-JSON
Content-Type. A response that passes _intercept (content_type=None) could then
raise aiohttp.ContentTypeError in api.py, crashing load_*/send_command.

Align all call-sites to resp.json(content_type=None).

Test: a strict response that rejects json() without content_type=None, exercised
through load_appliances and load_commands.
tis24dev added a commit that referenced this pull request Jun 25, 2026
)

- oauth._remote_descriptor: defensive parse (bad JSON, non-dict payload, a
  non-numeric or overflowing `ver`) so a malformed Salesforce descriptor stays
  inert -- detection falls back to None instead of raising a hard login failure (#8).
- oauth.detect_progressive_otp: return None when the ViewState id ("vid") is
  absent, mirroring the csrf/authorization guard; an empty vid is sent in every
  /apexremote ctx and would be rejected with a confusing error (#7).
- config_flow._async_close_mfa_client: also clear _mfa_data (the cached plaintext
  password) and _mfa_reauth_entry, so credentials/state are not left reachable on
  the flow object after success, abort, or async_remove (#9).
- hon_client: thread the recorded phase into classify(err, phase=...) at the
  setup / MFA-verify / MFA-send failure sites (defensive; no auth phase is
  timeout-mapped today, so it is future-proofing of the diagnostics wiring) (#10).
- tests: RUF059 unused-unpack rename; mutation-proven coverage for each fix.
tis24dev added a commit that referenced this pull request Jun 28, 2026
…els + harden test stub

- Greptile P2 (duplicate labels lose codes): HonProgramOptionSelect built a reverse
  label->raw map that collapsed when two exposed raw codes shared a label
  (DRY_LEVEL_LABELS_TD maps e.g. 1 & 12 both to "iron_dry"), making one code
  unreachable and breaking the select round-trip on a device exposing both tiers.
  __init__ is now collision-aware: only the colliding labels are suffixed with their
  raw code, so every exposed code stays selectable and the reverse map is injective.
  Non-colliding labels are untouched, so the common case (every real WM + erpayo's TD
  dryLevel[12,13,14]) is byte-for-byte unchanged and keeps its translatable state keys.
- CodeRabbit (#8): behavioral tests that a device exposing label-colliding codes keeps
  both selectable and round-trippable, plus a no-collision guard (mutation-proven).
- CodeRabbit (#9/#10, partial): completed test_wash_option_params.py's HA stub
  (force-assign a complete CoordinatorEntity with hass/async_write_ha_state/available,
  mirroring test_ac_write_path.py) so the module is collection-order-robust and never
  poisons other entity-constructing modules. The repo-wide conftest cleanup is left as
  a separate follow-up.

Built via 3 convergent constructors + 3-lens refuter pool to HOLD. 904 passed, 1
skipped (local + CI-clean). The duplicate-label fix is theoretical on known models
(no real device exposes both dryLevel tiers); shipped as forward robustness.
tis24dev added a commit that referenced this pull request Jun 28, 2026
CodeRabbit: test_catalog_keys_and_params_unique_within_type guarded only param uniqueness, though its comment also claimed to cover unique_id-suffix collisions. The option entity unique_id is f"{appliance_id}_opt_{key}" scoped per platform, so two controls of the same platform sharing a key collide. Add a per-platform key uniqueness assertion (cross-platform key reuse is fine -- different entity domains). Mutation-proven: a duplicate key with a distinct param fails the key check while the param check still passes. 907 passed, 1 skipped.
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.

2 participants