Skip to content

Change DHW comfort switch to select#883

Open
bouwew wants to merge 47 commits into
mainfrom
dhw_update
Open

Change DHW comfort switch to select#883
bouwew wants to merge 47 commits into
mainfrom
dhw_update

Conversation

@bouwew

@bouwew bouwew commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Heater devices now expose explicit DHW mode selection with modes “comfort” and “off”, using unified mode/state fields.
  • Bug Fixes

    • Legacy DHW switch/toggle representation was removed and replaced by improved DHW mode detection, initialization, and selection behavior (including heater-central devices).
  • Tests

    • Updated fixtures and async tests across many scenarios; adjusted expected entity counts and removed legacy DHW toggle expectations.
  • Documentation

    • Updated release notes for v1.12.0 to reflect the DHW mode selector migration and bumped the package version.

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@bouwew, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 23 minutes and 27 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 639a5a31-0708-4d77-9b7d-c5c0bfdb5341

📥 Commits

Reviewing files that changed from the base of the PR and between cd5d762 and 5ed3b01.

📒 Files selected for processing (1)
  • plugwise/data.py
📝 Walkthrough

Walkthrough

Refactors DHW comfort mode control from a switch-based model (dhw_cm_switch) to a measurement-based select-like mode. Updates constants to add dhw_mode type field and remove legacy toggle mapping; adds helper methods to collect and map DHW modes with XML→comfort/off translation; removes dhw_cm_switch from switch models; applies conditional gateway entity field migration; updates all test fixtures to represent DHW state as dhw_modes array plus select_dhw_mode or water_heater_mode; adjusts test assertions and bumps version.

Changes

DHW Mode Selection for Heater Central Devices

Layer / File(s) Summary
Constants, types, and data schema
plugwise/constants.py
Removes "dhw_cm_switch" from SwitchType Literal, removes the TOGGLES constant, and adds dhw_mode: str field to GwEntityData TypedDict alongside existing dhw_modes and select_dhw_mode.
DHW collection and measurement selection logic
plugwise/helper.py
Adds _collect_dhw_modes() to initialize allowed modes from actuators or defaults; refactors _appliance_measurements() to preserve original measurement keys and delegate select_dhw_mode to new _select_dhw_mode() helper; implements _select_dhw_mode() with XML on/off→comfort/off mapping; refactors _get_toggle_state() to dispatch by toggle name for cooling_enabled and domestic_hot_water_comfort_mode initialization.
Smile API updates and gateway entity migration
plugwise/smile.py, plugwise/data.py, plugwise/__init__.py
Removes dhw_cm_switch from model_to_switch_items() mapping; adds conditional gateway entity transformation to migrate select_dhw_mode→dhw_mode when max_dhw_temperature exists; refactors Anna P1 detection to check model before iterating meters.
Test fixture and test-data updates
tests/data/adam/*, tests/data/anna/*, fixtures/*
Updates all heater_central and water-heater device records across 35+ fixture files to include dhw_modes array and either select_dhw_mode (select-based entities) or water_heater_mode (gateway remap), removing dhw_cm_switch from switches blocks.
Test assertions and release metadata
tests/test_adam.py, tests/test_anna.py, CHANGELOG.md, pyproject.toml
Adjusts entity count assertions across Adam/Anna test scenarios (mostly +1 increment for new select entity; Loria tests -1 for removed switch); removes obsolete dhw_cm_switch tinker_switch assertion; adds v1.12.0 changelog entry; bumps version to 1.12.0.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 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 'Change DHW comfort switch to select' clearly and specifically describes the primary change in the PR: converting a DHW (domestic hot water) comfort mode switch control to a select/dropdown control mechanism.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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 dhw_update

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot requested a review from CoMPaTech June 10, 2026 06:24
@coderabbitai coderabbitai Bot added the enhancement New feature or request label Jun 10, 2026

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
plugwise/helper.py (1)

35-35: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove unused import.

The TOGGLES constant is imported but never used in this file.

🧹 Proposed fix
-    TOGGLES,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugwise/helper.py` at line 35, Remove the unused import TOGGLES from the
import list in this module: locate the import statement that includes TOGGLES in
plugwise/helper.py and delete TOGGLES (or the whole import if it becomes empty)
so the file no longer imports an unused symbol.

Source: Pipeline failures

tests/test_adam.py (1)

120-123: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Test will fail: dhw_cm_switch removed from fixture.

According to the layer description, the adam_plus_anna_new fixture (loaded at line 35) has had dhw_cm_switch entries removed from switches objects. This test attempts to tinker with dhw_cm_switch on the heater_central device and expects success (line 123 assert switch_change), but the switch no longer exists in the fixture data.

🔧 Suggested fix options

Option 1: Remove the obsolete switch test

-        switch_change = await self.tinker_switch(
-            api,
-            "056ee145a816487eaa69243c3280f8bf",
-            model="dhw_cm_switch",
-        )
-        assert switch_change

Option 2: Update test to verify the new select entity

Replace the switch test with a test for the new select_dhw_mode functionality, if a corresponding test helper exists.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_adam.py` around lines 120 - 123, The test fails because the
adam_plus_anna_new fixture no longer includes the dhw_cm_switch entity; update
tests/test_adam.py by removing the obsolete switch assertion or replacing it
with a check for the new select entity (e.g., change the call that targets
model="dhw_cm_switch" on device "056ee145a816487eaa69243c3280f8bf" to instead
target the new select entity name such as "select_dhw_mode" and assert its
behavior), ensuring you update the invoked test helper call and the final
assertion (currently "assert switch_change") to match the new entity and
expected return value.
🧹 Nitpick comments (1)
plugwise/helper.py (1)

467-480: ⚡ Quick win

Clarify old_measurement initialization and scope.

The variable old_measurement is assigned on line 468 only when new_name exists, but it's referenced unconditionally on line 477. While this appears safe in practice (both domestic_hot_water_mode and domestic_hot_water_comfort_mode have DATA mappings with names), the logic is fragile and not immediately obvious.

♻️ Proposed refactor for clarity
 def _appliance_measurements(
     self,
     appliance: etree.Element,
     data: GwEntityData,
     measurements: dict[str, DATA | UOM],
 ) -> None:
     """Helper-function for _get_measurement_data() - collect appliance measurement data."""
     for measurement, attrs in measurements.items():
         p_locator = f'.//logs/point_log[type="{measurement}"]/period/measurement'
         if (appl_p_loc := appliance.find(p_locator)) is not None:
             if skip_obsolete_measurements(appliance, measurement):
                 continue
 
+            old_measurement = measurement
             if new_name := getattr(attrs, ATTR_NAME, None):
-                old_measurement = measurement
                 measurement = new_name
 
             match measurement:
                 case "elga_status_code":
                     data["elga_status_code"] = int(appl_p_loc.text)
                 case "select_dhw_mode":
                     if self._dhw_allowed_modes:
                         data["select_dhw_mode"] = appl_p_loc.text
                         if old_measurement == "domestic_hot_water_comfort_mode":
                             data["select_dhw_mode"] = (
                                 "comfort" if appl_p_loc.text == "on" else "off"
                             )

This ensures old_measurement is always defined before the match statement.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugwise/helper.py` around lines 467 - 480, old_measurement is only set when
new_name exists but is referenced later unconditionally in the match branch for
"select_dhw_mode"; initialize old_measurement to the current measurement before
the conditional that may rename it so it always has a value (ensure the block
using ATTR_NAME still updates measurement when new_name is present and preserves
old_measurement for later checks), referencing the existing symbols measurement,
old_measurement, ATTR_NAME, appl_p_loc, and self._dhw_allowed_modes to locate
and adjust the logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugwise/helper.py`:
- Around line 267-278: _collect_dhw_modes calls _get_toggle_state with None for
the data parameter which violates the declared GwEntityData (non-Optional) type;
either make _get_toggle_state accept Optional[GwEntityData] (update its
signature and handle None inside the function) or change the call in
_collect_dhw_modes to pass an empty dict/appropriate GwEntityData-shaped empty
value instead of None for the "domestic_hot_water_comfort_mode" /
"dhw_cm_switch" call so the types remain consistent.

---

Outside diff comments:
In `@plugwise/helper.py`:
- Line 35: Remove the unused import TOGGLES from the import list in this module:
locate the import statement that includes TOGGLES in plugwise/helper.py and
delete TOGGLES (or the whole import if it becomes empty) so the file no longer
imports an unused symbol.

In `@tests/test_adam.py`:
- Around line 120-123: The test fails because the adam_plus_anna_new fixture no
longer includes the dhw_cm_switch entity; update tests/test_adam.py by removing
the obsolete switch assertion or replacing it with a check for the new select
entity (e.g., change the call that targets model="dhw_cm_switch" on device
"056ee145a816487eaa69243c3280f8bf" to instead target the new select entity name
such as "select_dhw_mode" and assert its behavior), ensuring you update the
invoked test helper call and the final assertion (currently "assert
switch_change") to match the new entity and expected return value.

---

Nitpick comments:
In `@plugwise/helper.py`:
- Around line 467-480: old_measurement is only set when new_name exists but is
referenced later unconditionally in the match branch for "select_dhw_mode";
initialize old_measurement to the current measurement before the conditional
that may rename it so it always has a value (ensure the block using ATTR_NAME
still updates measurement when new_name is present and preserves old_measurement
for later checks), referencing the existing symbols measurement,
old_measurement, ATTR_NAME, appl_p_loc, and self._dhw_allowed_modes to locate
and adjust the logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c3649d18-c70a-480a-8b38-f2124773ded9

📥 Commits

Reviewing files that changed from the base of the PR and between d06304e and 8209bef.

📒 Files selected for processing (10)
  • plugwise/constants.py
  • plugwise/helper.py
  • tests/data/adam/adam_bad_thermostat.json
  • tests/data/adam/adam_heatpump_cooling.json
  • tests/data/adam/adam_jip.json
  • tests/data/adam/adam_onoff_cooling_fake_firmware.json
  • tests/data/adam/adam_plus_anna.json
  • tests/data/adam/adam_plus_anna_new.json
  • tests/data/adam/adam_plus_anna_new_regulation_off.json
  • tests/test_adam.py

Comment thread plugwise/helper.py
@codecov

codecov Bot commented Jun 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (a807089) to head (5ed3b01).

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #883   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           21        21           
  Lines         3464      3475   +11     
=========================================
+ Hits          3464      3475   +11     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_anna.py`:
- Line 443: The assertion at tests/test_anna.py that compares self.entity_items
to a hardcoded value (assert self.entity_items == 67) is invalid because the
fixtures' schema doesn't match that top-level count; change the test to derive
the expected entity count using the same enumeration logic used elsewhere in the
file (reuse the same iteration/filtering that computes self.entity_items in the
other checks around lines ~443/508/531/554) instead of a hardcoded literal, or
compute expected by walking each fixture's entries (including
dhw_modes/select_dhw_mode) with the same function/method that populates
self.entity_items so the assertion compares like-for-like; update only the
assertion site in tests/test_anna.py to use that computed expected value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 60e13c73-fc5e-4b72-9d24-5d7fc876c892

📥 Commits

Reviewing files that changed from the base of the PR and between 8209bef and e9b03ca.

📒 Files selected for processing (50)
  • fixtures/adam_bad_thermostat/data.json
  • fixtures/adam_heatpump_cooling/data.json
  • fixtures/adam_jip/data.json
  • fixtures/adam_onoff_cooling_fake_firmware/data.json
  • fixtures/adam_plus_anna/data.json
  • fixtures/adam_plus_anna_new/data.json
  • fixtures/adam_plus_anna_new_regulation_off/data.json
  • fixtures/anna_elga_2/data.json
  • fixtures/anna_elga_2_cooling/data.json
  • fixtures/anna_elga_2_schedule_off/data.json
  • fixtures/anna_elga_no_cooling/data.json
  • fixtures/anna_heatpump_cooling/data.json
  • fixtures/anna_heatpump_cooling_fake_firmware/data.json
  • fixtures/anna_heatpump_heating/data.json
  • fixtures/anna_loria_cooling_active/data.json
  • fixtures/anna_loria_driessens/data.json
  • fixtures/anna_loria_heating_idle/data.json
  • fixtures/anna_p1/data.json
  • fixtures/anna_v4/data.json
  • fixtures/anna_v4_dhw/data.json
  • fixtures/anna_v4_no_tag/data.json
  • fixtures/m_adam_cooling/data.json
  • fixtures/m_adam_heating/data.json
  • fixtures/m_adam_heating_off_schedule/data.json
  • fixtures/m_adam_jip/data.json
  • fixtures/m_anna_heatpump_cooling/data.json
  • fixtures/m_anna_heatpump_idle/data.json
  • plugwise/constants.py
  • plugwise/helper.py
  • plugwise/smile.py
  • tests/data/adam/adam_heatpump_cooling.json
  • tests/data/anna/anna_elga_2.json
  • tests/data/anna/anna_elga_2_cooling.json
  • tests/data/anna/anna_elga_2_cooling_UPDATED_DATA.json
  • tests/data/anna/anna_elga_2_schedule_off.json
  • tests/data/anna/anna_elga_no_cooling.json
  • tests/data/anna/anna_heatpump_cooling.json
  • tests/data/anna/anna_heatpump_cooling_fake_firmware.json
  • tests/data/anna/anna_heatpump_heating.json
  • tests/data/anna/anna_heatpump_heating_UPDATED_DATA.json
  • tests/data/anna/anna_loria_cooling_active.json
  • tests/data/anna/anna_loria_driessens.json
  • tests/data/anna/anna_loria_heating_idle.json
  • tests/data/anna/anna_p1.json
  • tests/data/anna/anna_v4.json
  • tests/data/anna/anna_v4_UPDATED_DATA.json
  • tests/data/anna/anna_v4_dhw.json
  • tests/data/anna/anna_v4_no_tag.json
  • tests/test_adam.py
  • tests/test_anna.py
💤 Files with no reviewable changes (2)
  • plugwise/smile.py
  • plugwise/constants.py
✅ Files skipped from review due to trivial changes (5)
  • fixtures/anna_v4/data.json
  • tests/data/anna/anna_elga_2_cooling.json
  • fixtures/anna_heatpump_cooling/data.json
  • fixtures/m_anna_heatpump_idle/data.json
  • fixtures/adam_onoff_cooling_fake_firmware/data.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/data/adam/adam_heatpump_cooling.json
  • plugwise/helper.py

Comment thread tests/test_anna.py
@bouwew bouwew marked this pull request as ready for review June 13, 2026 09:14
@bouwew bouwew requested a review from a team as a code owner June 13, 2026 09:14

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/data/anna/anna_v4_dhw.json (1)

54-77: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Data inconsistency between DHW state sensors and water_heater_mode.

The fixture shows dhw_state: true and flame_state: true (lines 55-56), indicating the water heater is actively heating DHW, but water_heater_mode is set to "off" (line 77). These values are contradictory. The filename suffix _dhw further suggests this fixture should represent an active DHW scenario, not an off state.

Expected: water_heater_mode: "comfort" to match the active DHW state.

🔧 Proposed fix
-    "water_heater_mode": "off",
+    "water_heater_mode": "comfort",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/data/anna/anna_v4_dhw.json` around lines 54 - 77, The fixture has
inconsistent DHW sensor fields: dhw_state and flame_state are true but
water_heater_mode is "off"; update the water_heater_mode field in the JSON (the
"water_heater_mode" property) to "comfort" so it matches the active DHW scenario
(dhw_state/flame_state true and dhw_modes includes "comfort").
🧹 Nitpick comments (2)
plugwise/smile.py (1)

231-246: 💤 Low value

Deferred location validation may reduce error clarity.

The signature change to loc_id: str | None correctly reflects that DHW mode selection (lines 236-237) doesn't require a location ID, while schedule and zone profile operations do. However, the validation for required loc_id is now deferred to the called methods (set_schedule_state and set_zone_profile) rather than validated at the set_select entry point.

This means callers who forget to pass loc_id for schedule/profile operations will get an error message from the inner method rather than from the top-level handler, which may be less clear.

Consider adding early validation in set_select:

case "select_schedule" | "select_zone_profile":
    if loc_id is None:
        raise PlugwiseError(f"Plugwise: location required for {key}.")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugwise/smile.py` around lines 231 - 246, The set_select handler defers
loc_id validation to inner methods (set_schedule_state, set_zone_profile), so
add an early check in set_select for the cases that require a location: in the
match arm for "select_schedule" and "select_zone_profile" validate if loc_id is
None and raise PlugwiseError(f"Plugwise: location required for {key}.") before
calling set_schedule_state or set_zone_profile; leave the other branches
(set_dhw_mode, set_gateway_mode, set_regulation_mode) unchanged since they do
not need a loc_id.
plugwise/constants.py (1)

183-184: Clarify DHW mode mapping: domestic_hot_water_mode and domestic_hot_water_comfort_mode intentionally feed the same select_dhw_mode

Both entries in HEATER_CENTRAL_MEASUREMENTS map to the same payload key ("select_dhw_mode"), and that’s used as a compatibility normalization: domestic_hot_water_comfort_mode is specifically converted from toggle values (on/off) into comfort/off, and it’s also used as the fallback when actuator domestic_hot_water_mode_control_functionality doesn’t expose allowed modes. Add a short comment near the constants to document that these two measurements are alternate sources for the same DHW-mode concept.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugwise/constants.py` around lines 183 - 184, Add a short clarifying comment
next to the HEATER_CENTRAL_MEASUREMENTS constant explaining that
"domestic_hot_water_mode" and "domestic_hot_water_comfort_mode" intentionally
map to the same payload key "select_dhw_mode" as alternate sources for the same
DHW-mode concept; note that domestic_hot_water_comfort_mode is derived from
toggle values (on/off → comfort/off) and that it serves as a fallback when the
actuator key domestic_hot_water_mode_control_functionality does not expose
allowed modes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugwise/data.py`:
- Around line 81-86: The remapping currently gated on "max_dhw_temperature"
should instead trigger when the DHW mode info is present; change the condition
that reads if "max_dhw_temperature" in entity: to check for DHW mode capability
such as if "select_dhw_mode" in entity (or if entity.get("select_dhw_mode") is
not None / or presence of "dhw_modes"), so the existing block that pops
"select_dhw_mode" and sets "water_heater_mode" runs whenever select_dhw_mode/DHW
mode support is present.

In `@plugwise/smile.py`:
- Around line 340-345: The validation is unreachable because the combined
condition checks "or loc_id is None" with new_state, causing the generic
PlugwiseError to be raised before the specific loc_id check; change the logic so
you first validate new_state against (STATE_OFF, STATE_ON) and raise
PlugwiseError if invalid, then separately check loc_id is None and raise the
more specific PlugwiseError message; update the condition that currently uses
"if new_state not in (STATE_OFF, STATE_ON) or loc_id is None:" to only test
new_state, and keep the existing loc_id None branch as the second independent
check.

---

Outside diff comments:
In `@tests/data/anna/anna_v4_dhw.json`:
- Around line 54-77: The fixture has inconsistent DHW sensor fields: dhw_state
and flame_state are true but water_heater_mode is "off"; update the
water_heater_mode field in the JSON (the "water_heater_mode" property) to
"comfort" so it matches the active DHW scenario (dhw_state/flame_state true and
dhw_modes includes "comfort").

---

Nitpick comments:
In `@plugwise/constants.py`:
- Around line 183-184: Add a short clarifying comment next to the
HEATER_CENTRAL_MEASUREMENTS constant explaining that "domestic_hot_water_mode"
and "domestic_hot_water_comfort_mode" intentionally map to the same payload key
"select_dhw_mode" as alternate sources for the same DHW-mode concept; note that
domestic_hot_water_comfort_mode is derived from toggle values (on/off →
comfort/off) and that it serves as a fallback when the actuator key
domestic_hot_water_mode_control_functionality does not expose allowed modes.

In `@plugwise/smile.py`:
- Around line 231-246: The set_select handler defers loc_id validation to inner
methods (set_schedule_state, set_zone_profile), so add an early check in
set_select for the cases that require a location: in the match arm for
"select_schedule" and "select_zone_profile" validate if loc_id is None and raise
PlugwiseError(f"Plugwise: location required for {key}.") before calling
set_schedule_state or set_zone_profile; leave the other branches (set_dhw_mode,
set_gateway_mode, set_regulation_mode) unchanged since they do not need a
loc_id.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 31c1a149-0f03-4f9c-9e0b-cb9bb8d0f368

📥 Commits

Reviewing files that changed from the base of the PR and between e9b03ca and 89a810a.

📒 Files selected for processing (35)
  • CHANGELOG.md
  • fixtures/adam_bad_thermostat/data.json
  • fixtures/adam_heatpump_cooling/data.json
  • fixtures/adam_jip/data.json
  • fixtures/adam_onoff_cooling_fake_firmware/data.json
  • fixtures/anna_elga_no_cooling/data.json
  • fixtures/anna_heatpump_heating/data.json
  • fixtures/anna_loria_cooling_active/data.json
  • fixtures/anna_loria_driessens/data.json
  • fixtures/anna_loria_heating_idle/data.json
  • fixtures/anna_v4/data.json
  • fixtures/anna_v4_dhw/data.json
  • fixtures/anna_v4_no_tag/data.json
  • fixtures/m_adam_jip/data.json
  • fixtures/m_anna_heatpump_cooling/data.json
  • fixtures/m_anna_heatpump_idle/data.json
  • plugwise/__init__.py
  • plugwise/constants.py
  • plugwise/data.py
  • plugwise/helper.py
  • plugwise/smile.py
  • pyproject.toml
  • tests/data/adam/adam_bad_thermostat.json
  • tests/data/adam/adam_heatpump_cooling.json
  • tests/data/adam/adam_jip.json
  • tests/data/adam/adam_onoff_cooling_fake_firmware.json
  • tests/data/anna/anna_elga_no_cooling.json
  • tests/data/anna/anna_heatpump_heating.json
  • tests/data/anna/anna_loria_cooling_active.json
  • tests/data/anna/anna_loria_driessens.json
  • tests/data/anna/anna_loria_heating_idle.json
  • tests/data/anna/anna_v4.json
  • tests/data/anna/anna_v4_UPDATED_DATA.json
  • tests/data/anna/anna_v4_dhw.json
  • tests/data/anna/anna_v4_no_tag.json
✅ Files skipped from review due to trivial changes (11)
  • pyproject.toml
  • CHANGELOG.md
  • tests/data/anna/anna_loria_cooling_active.json
  • tests/data/adam/adam_jip.json
  • fixtures/m_anna_heatpump_cooling/data.json
  • plugwise/init.py
  • tests/data/adam/adam_heatpump_cooling.json
  • fixtures/anna_v4_no_tag/data.json
  • fixtures/anna_loria_heating_idle/data.json
  • tests/data/adam/adam_onoff_cooling_fake_firmware.json
  • fixtures/anna_heatpump_heating/data.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/data/adam/adam_bad_thermostat.json
  • plugwise/helper.py

Comment thread plugwise/data.py Outdated
Comment on lines +81 to +86
# Replace select_dhw_mode with water_heater_mode when applicable
if "max_dhw_temperature" in entity:
mode = entity.get("select_dhw_mode")
if mode is not None:
entity.pop("select_dhw_mode")
entity["water_heater_mode"] = mode

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if all heater_central entities with DHW state also have max_dhw_temperature

# Find heater_central entities in test fixtures
rg -A5 '"dev_class": "heater_central"' fixtures/ --json | \
  jq -r 'select(.type=="match") | .data.path.text' | sort -u | while read fixture; do
  echo "=== $fixture ==="
  jq 'to_entries[] | select(.value.dev_class == "heater_central") | 
    {id: .key, has_dhw_state: (.value.binary_sensors.dhw_state != null), has_max_dhw: (.value.max_dhw_temperature != null), has_dhw_modes: (.value.dhw_modes != null), has_water_heater_mode: (.value.water_heater_mode != null)}' "$fixture"
done

Repository: plugwise/python-plugwise

Length of output: 6729


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) See if fixtures contain select_dhw_mode anywhere
echo "=== Files containing select_dhw_mode ==="
rg -n '"select_dhw_mode"' fixtures/ | head -n 50 || true

# 2) For the specific fixture JSONs that had dhw_modes but no max_dhw and no water_heater_mode,
#    check whether select_dhw_mode exists.
targets=(
  "fixtures/adam_plus_anna/data.json"
  "fixtures/adam_plus_anna_new/data.json"
  "fixtures/adam_plus_anna_new_regulation_off/data.json"
  "fixtures/anna_elga_2/data.json"
  "fixtures/anna_p1/data.json"
)

for f in "${targets[@]}"; do
  echo
  echo "=== Inspecting $f ==="
  jq -r '
    . as $root
    | to_entries[]
    | select(.value.dev_class == "heater_central")
    | {
        id: .key,
        has_max_dhw: (.value.max_dhw_temperature != null),
        has_select_dhw_mode: (.value.select_dhw_mode != null),
        has_water_heater_mode: (.value.water_heater_mode != null),
        has_dhw_modes: (.value.dhw_modes != null)
      }' "$f"
done

Repository: plugwise/python-plugwise

Length of output: 2082


Fix DHW mode remapping discriminator in plugwise/data.py (lines 81-86).
The remap select_dhw_modewater_heater_mode only runs when "max_dhw_temperature" in entity, but fixture data shows DHW mode support exists without max_dhw_temperature (e.g., fixtures/adam_plus_anna*/data.json, fixtures/anna_elga_2/data.json, fixtures/anna_p1/data.json: select_dhw_mode + dhw_modes present while water_heater_mode is missing). Update the condition to key off select_dhw_mode/DHW mode capability instead of max_dhw_temperature.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugwise/data.py` around lines 81 - 86, The remapping currently gated on
"max_dhw_temperature" should instead trigger when the DHW mode info is present;
change the condition that reads if "max_dhw_temperature" in entity: to check for
DHW mode capability such as if "select_dhw_mode" in entity (or if
entity.get("select_dhw_mode") is not None / or presence of "dhw_modes"), so the
existing block that pops "select_dhw_mode" and sets "water_heater_mode" runs
whenever select_dhw_mode/DHW mode support is present.

Comment thread plugwise/smile.py Outdated

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugwise/data.py`:
- Line 81: Update the comment that describes the DHW field remapping to use the
correct target field name. The comment currently references water_heater_mode
but should reference dhw_mode to accurately reflect the actual field mapping
being performed in the code. Change the comment text to state that
select_dhw_mode is mapped to dhw_mode.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cc4aa1f8-50e3-4c1a-a252-bc8a1e08c13a

📥 Commits

Reviewing files that changed from the base of the PR and between 89a810a and cd5d762.

📒 Files selected for processing (60)
  • CHANGELOG.md
  • fixtures/adam_bad_thermostat/data.json
  • fixtures/adam_heatpump_cooling/data.json
  • fixtures/adam_jip/data.json
  • fixtures/adam_onoff_cooling_fake_firmware/data.json
  • fixtures/adam_plus_anna/data.json
  • fixtures/adam_plus_anna_new/data.json
  • fixtures/adam_plus_anna_new_regulation_off/data.json
  • fixtures/anna_elga_2/data.json
  • fixtures/anna_elga_2_cooling/data.json
  • fixtures/anna_elga_2_schedule_off/data.json
  • fixtures/anna_elga_no_cooling/data.json
  • fixtures/anna_heatpump_cooling/data.json
  • fixtures/anna_heatpump_cooling_fake_firmware/data.json
  • fixtures/anna_heatpump_heating/data.json
  • fixtures/anna_loria_cooling_active/data.json
  • fixtures/anna_loria_driessens/data.json
  • fixtures/anna_loria_heating_idle/data.json
  • fixtures/anna_p1/data.json
  • fixtures/anna_v4/data.json
  • fixtures/anna_v4_dhw/data.json
  • fixtures/anna_v4_no_tag/data.json
  • fixtures/m_adam_cooling/data.json
  • fixtures/m_adam_heating/data.json
  • fixtures/m_adam_heating_off_schedule/data.json
  • fixtures/m_adam_jip/data.json
  • fixtures/m_anna_heatpump_cooling/data.json
  • fixtures/m_anna_heatpump_idle/data.json
  • plugwise/__init__.py
  • plugwise/constants.py
  • plugwise/data.py
  • plugwise/helper.py
  • plugwise/smile.py
  • pyproject.toml
  • tests/data/adam/adam_bad_thermostat.json
  • tests/data/adam/adam_heatpump_cooling.json
  • tests/data/adam/adam_jip.json
  • tests/data/adam/adam_onoff_cooling_fake_firmware.json
  • tests/data/adam/adam_plus_anna.json
  • tests/data/adam/adam_plus_anna_new.json
  • tests/data/adam/adam_plus_anna_new_regulation_off.json
  • tests/data/anna/anna_elga_2.json
  • tests/data/anna/anna_elga_2_cooling.json
  • tests/data/anna/anna_elga_2_cooling_UPDATED_DATA.json
  • tests/data/anna/anna_elga_2_schedule_off.json
  • tests/data/anna/anna_elga_no_cooling.json
  • tests/data/anna/anna_heatpump_cooling.json
  • tests/data/anna/anna_heatpump_cooling_fake_firmware.json
  • tests/data/anna/anna_heatpump_heating.json
  • tests/data/anna/anna_heatpump_heating_UPDATED_DATA.json
  • tests/data/anna/anna_loria_cooling_active.json
  • tests/data/anna/anna_loria_driessens.json
  • tests/data/anna/anna_loria_heating_idle.json
  • tests/data/anna/anna_p1.json
  • tests/data/anna/anna_v4.json
  • tests/data/anna/anna_v4_UPDATED_DATA.json
  • tests/data/anna/anna_v4_dhw.json
  • tests/data/anna/anna_v4_no_tag.json
  • tests/test_adam.py
  • tests/test_anna.py
💤 Files with no reviewable changes (1)
  • plugwise/smile.py
✅ Files skipped from review due to trivial changes (17)
  • tests/data/anna/anna_elga_no_cooling.json
  • fixtures/anna_elga_2_cooling/data.json
  • tests/data/anna/anna_v4_UPDATED_DATA.json
  • fixtures/anna_heatpump_heating/data.json
  • tests/data/anna/anna_elga_2.json
  • tests/data/anna/anna_loria_heating_idle.json
  • tests/data/anna/anna_v4.json
  • tests/data/adam/adam_plus_anna_new_regulation_off.json
  • tests/data/anna/anna_heatpump_heating_UPDATED_DATA.json
  • tests/data/anna/anna_heatpump_cooling.json
  • pyproject.toml
  • tests/data/anna/anna_elga_2_cooling.json
  • tests/data/anna/anna_elga_2_schedule_off.json
  • fixtures/anna_v4/data.json
  • fixtures/anna_v4_no_tag/data.json
  • fixtures/adam_plus_anna/data.json
  • fixtures/anna_loria_heating_idle/data.json
🚧 Files skipped from review as they are similar to previous changes (21)
  • CHANGELOG.md
  • fixtures/m_anna_heatpump_idle/data.json
  • fixtures/m_anna_heatpump_cooling/data.json
  • fixtures/adam_jip/data.json
  • tests/data/adam/adam_plus_anna.json
  • fixtures/anna_p1/data.json
  • plugwise/init.py
  • fixtures/m_adam_heating_off_schedule/data.json
  • fixtures/anna_elga_2_schedule_off/data.json
  • fixtures/anna_elga_2/data.json
  • tests/data/adam/adam_plus_anna_new.json
  • tests/data/adam/adam_onoff_cooling_fake_firmware.json
  • tests/data/anna/anna_elga_2_cooling_UPDATED_DATA.json
  • tests/data/anna/anna_p1.json
  • fixtures/adam_plus_anna_new/data.json
  • fixtures/anna_heatpump_cooling/data.json
  • tests/data/anna/anna_heatpump_cooling_fake_firmware.json
  • fixtures/anna_loria_driessens/data.json
  • tests/test_adam.py
  • tests/test_anna.py
  • plugwise/helper.py

Comment thread plugwise/data.py Outdated
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants