Skip to content

Add PyKumoBase.has_profile() to expose profile-populated state#72

Merged
dlarrick merged 2 commits into
dlarrick:masterfrom
d-walsh:add-has-profile
Jun 14, 2026
Merged

Add PyKumoBase.has_profile() to expose profile-populated state#72
dlarrick merged 2 commits into
dlarrick:masterfrom
d-walsh:add-has-profile

Conversation

@d-walsh

@d-walsh d-walsh commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Add a one-line public method has_profile() -> bool to PyKumoBase that tells callers whether the unit profile has been populated from a successful poll.

Motivation

_profile starts as {} at construction and is populated after the first successful update_status() call. Capability methods — has_auto_mode(), has_heat_mode(), get_fan_speeds(), get_vane_directions(), etc. — all read _profile and return silent defaults (False / a hardcoded fallback list) when the profile is empty. A truthiness check on their return values cannot distinguish real hardware data from those defaults.

Consumers that derive capability lists (e.g. which HVAC modes are supported) and cache the result need a reliable way to know whether the profile is populated before trusting capability output. Without this, capabilities derived before the first successful poll (e.g. when the WiFi adapter is offline at startup) are silently wrong and never corrected.

This is the exact scenario described in hass-kumo PR #223, which currently works around it via self._pykumo._profile — a direct reach into a private attribute.

Change

# py_kumo_base.py — PyKumoBase

def has_profile(self) -> bool:
    """Return True if the unit profile has been populated from a successful poll.
    ...
    """
    return bool(self._profile)

3 unit tests added in tests/test_py_kumo_base.py.

Relationship to hass-kumo

hass-kumo PR dlarrick/hass-kumo#223 adds a profile-populated guard before refreshing HVAC capabilities. Once this method is available and released, that PR can replace self._pykumo._profile with self._pykumo.has_profile() and bump the pykumo requirement accordingly.

Consumers such as hass-kumo need to know whether the unit profile has
been populated from a successful poll before relying on capability
methods (has_auto_mode(), has_heat_mode(), get_fan_speeds(), etc.).
Those methods return silent defaults (False / a fallback list) when
_profile is empty, so a truthiness check on their return value cannot
distinguish real hardware data from the defaults.

Add has_profile() -> bool to PyKumoBase, implemented as bool(self._profile).
The profile is initialised to {} at construction and populated after the
first successful update_status() call; this provides a clean, stable
predicate without exposing the internal dict.

Add three unit tests covering the empty, populated, and reset cases.

Enables hass-kumo PR #223 to replace its direct _profile access with
the new public method once this is released.

Co-Authored-By: Claude <noreply@anthropic.com>
d-walsh added a commit to d-walsh/hass-kumo-1 that referenced this pull request Jun 8, 2026
The profile-populated guard currently accesses pykumo's private
_profile attribute directly. dlarrick/pykumo#72 adds a public
has_profile() method for exactly this purpose. Add a TODO comment
so the migration path is clear once pykumo cuts a release with that
method and the requirement can be bumped.

Co-Authored-By: Claude <noreply@anthropic.com>
@dlarrick dlarrick requested a review from Copilot June 9, 2026 11:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR exposes whether a unit’s capability profile has been populated by adding a small public API (PyKumoBase.has_profile()) so consumers can avoid caching capability defaults before the first successful poll.

Changes:

  • Add PyKumoBase.has_profile() -> bool returning whether _profile is non-empty.
  • Add unit tests covering the populated/unpopulated profile state transitions.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
pykumo/py_kumo_base.py Adds has_profile() to let callers detect whether capability data is real vs fallback defaults.
tests/test_py_kumo_base.py Adds tests validating has_profile() before/after profile population and after clearing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/test_py_kumo_base.py
Neither import was referenced in any test body; removing them
silences the ruff F401 warning and makes pre-commit pass cleanly.

Co-Authored-By: Claude <noreply@anthropic.com>
@d-walsh

d-walsh commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

Removed the unused MagicMock and patch imports; pre-commit/ruff clean now.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@dlarrick dlarrick merged commit 0401691 into dlarrick:master Jun 14, 2026
2 checks passed
d-walsh added a commit to d-walsh/hass-kumo-1 that referenced this pull request Jun 15, 2026
pykumo 0.5.2 ships PyKumoBase.has_profile() (merged from dlarrick/pykumo#72),
replacing the private _profile attribute access that was guarded with a noqa.

- manifest.json: pykumo>=0.5.0 → pykumo>=0.5.2
- climate.py _refresh_capabilities(): replace `if not self._pykumo._profile:`
  (with noqa: SLF001 and TODO comment) with `if not self._pykumo.has_profile()`

Co-Authored-By: Claude <noreply@anthropic.com>
dlarrick pushed a commit to dlarrick/hass-kumo that referenced this pull request Jun 16, 2026
* climate: refresh HVAC capabilities on every coordinator poll

Fixes the silent capability-strip bug reported in #105: when a unit's
WiFi adapter is offline at HA startup, pykumo returns empty profiles and
`__init__` caches `_hvac_modes = [OFF, COOL]` permanently.  Heat-cool
commands are then silently rejected until the next HA restart, even after
the adapter reconnects.

Changes:
- Extract the capability derivation block into `_refresh_capabilities()`.
- Call it from `__init__` (same behavior as before for online units) and
  from `update()` after every successful coordinator poll.
- Use an **upgrade-only** merge for `_hvac_modes`: modes are only ever
  added, never removed.  A transient poll failure cannot strip a
  capability that was already confirmed.  `fan_modes` and `swing_modes`
  are refreshed from the live profile unconditionally (pykumo preserves
  the last-good profile across failures, so this is always safe).
- Add a DEBUG log line after each refresh so capability changes are
  visible in HA logs without enabling extra verbosity.

Co-Authored-By: Claude <noreply@anthropic.com>

* climate: gate _refresh_capabilities on non-empty pykumo profile

pykumo initialises _profile to {} and only populates it after a
successful network poll.  The previous code guarded fan_modes with
`if fan_speeds:`, but get_fan_speeds() falls back to a hard-coded
5-item list when numberOfFanSpeeds is absent (KeyError → speeds = 5),
so the guard always passed and the stale default list was cached as
real hardware data.

Fix: skip all capability derivation when _profile is empty.  The
entity keeps its safe init defaults ([OFF, COOL], empty fan/swing
lists) until the first successful poll populates the profile.  Once
populated, the existing upgrade-only logic for hvac_modes and the
overwrite logic for fan/swing modes work correctly.

Co-Authored-By: Claude <noreply@anthropic.com>

* climate: note TODO to switch to has_profile() once pykumo#72 releases

The profile-populated guard currently accesses pykumo's private
_profile attribute directly. dlarrick/pykumo#72 adds a public
has_profile() method for exactly this purpose. Add a TODO comment
so the migration path is clear once pykumo cuts a release with that
method and the requirement can be bumped.

Co-Authored-By: Claude <noreply@anthropic.com>

* climate: fix _refresh_capabilities docstring for fan/swing update logic

The previous docstring said fan_modes/swing_modes are overwritten on
every populated-profile call.  The code actually uses a truthy guard
(if fan_speeds: / if vane_dirs:) so a transient empty read never
clobbers a previously confirmed list.  Update the docstring to match.
No logic changes.

Co-Authored-By: Claude <noreply@anthropic.com>

* Bump pykumo to >=0.5.2 and migrate _profile to has_profile()

pykumo 0.5.2 ships PyKumoBase.has_profile() (merged from dlarrick/pykumo#72),
replacing the private _profile attribute access that was guarded with a noqa.

- manifest.json: pykumo>=0.5.0 → pykumo>=0.5.2
- climate.py _refresh_capabilities(): replace `if not self._pykumo._profile:`
  (with noqa: SLF001 and TODO comment) with `if not self._pykumo.has_profile()`

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: d-walsh <2351963+d-walsh@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
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.

3 participants