Release v2.4.0 - #5
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe 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. ChangesManifest Configuration
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~2 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
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.
…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.
…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.
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.
…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.
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.
…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.
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.
#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.
…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).
Automated release PR for
v2.4.0.Merge this PR with squash only. The post-merge workflow will move
devto the squash commit, recreatev2.4.0on that commit, and publish the release.Summary by CodeRabbit