Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trying solution for Core issue #132479 #661

Merged
merged 32 commits into from
Dec 8, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2f2461c
Add updated userdata for Anna Elga cooling change to heating
bouwew Dec 6, 2024
fc76fef
Extend related test
bouwew Dec 6, 2024
ec9142e
Add related test-data json-file
bouwew Dec 6, 2024
a830c32
Correct directory name
bouwew Dec 6, 2024
07ecca3
Correct related test-case
bouwew Dec 6, 2024
c2e06ab
Userdata fixes
bouwew Dec 6, 2024
e46042e
Assert fix
bouwew Dec 6, 2024
74509da
Improve helper.py
bouwew Dec 6, 2024
5b8baf2
test_anna: fix to smile._cooling_enabled
bouwew Dec 7, 2024
2a90f25
Make _cooling_enabled for Smile class
bouwew Dec 7, 2024
7e7930e
test_anna: also fix to smile._cooling_active
bouwew Dec 7, 2024
16356c9
Test-adam also add/improve cooling asserts
bouwew Dec 7, 2024
45adc06
Disable setting self._cooling_enabled in smile.py
bouwew Dec 7, 2024
8293aa5
Extend test_init output
bouwew Dec 7, 2024
30d52fa
Revert back to self._cooling_*
bouwew Dec 7, 2024
29fe41c
Adam: add cooling_enabled when not in xml-data
bouwew Dec 7, 2024
f0b0cf3
Clean up unused code, clarify function via comment
bouwew Dec 7, 2024
161f5db
Save updated fixture and test-json files
bouwew Dec 7, 2024
12d2566
Revert adding self._cooling_enabled at top-level
bouwew Dec 7, 2024
2b69b2a
Add back setting self._cooling_enabled at top-level
bouwew Dec 7, 2024
62d910c
Another revert
bouwew Dec 7, 2024
ce6176b
Clean up
bouwew Dec 7, 2024
3463856
Add elga_2 tinker_thermostat test
bouwew Dec 7, 2024
573298c
Correct-extend tinker_thermostat_temp()
bouwew Dec 7, 2024
7c6901d
Adapt relevant testcases
bouwew Dec 7, 2024
c2deae7
Elga_2_cooling: tests again after async_update
bouwew Dec 7, 2024
74931e4
Update CHANGELOG
bouwew Dec 7, 2024
4bb7b95
Bump to v1.6.3a0 test-version
bouwew Dec 7, 2024
e654a08
Fix complexity
bouwew Dec 7, 2024
6c4339c
Fix to python 3.13.0
bouwew Dec 7, 2024
66e3ab2
Set cooling_enabled binary_sensor for Loria/Thermastage too
bouwew Dec 8, 2024
54ef0ce
Set to v1.6.3 release-version
bouwew Dec 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ name: Latest commit

env:
CACHE_VERSION: 11
DEFAULT_PYTHON: "3.13"
DEFAULT_PYTHON: "3.13.0"
PRE_COMMIT_HOME: ~/.cache/pre-commit

on:
Expand Down Expand Up @@ -173,7 +173,7 @@ jobs:
needs: commitcheck
strategy:
matrix:
python-version: ["3.13", "3.12"]
python-version: ["3.13.0", "3.12"]
steps:
- name: Check out committed code
uses: actions/checkout@v4
Expand Down Expand Up @@ -213,7 +213,7 @@ jobs:
needs: prepare-test-cache
strategy:
matrix:
python-version: ["3.13", "3.12"]
python-version: ["3.13.0", "3.12"]

steps:
- name: Check out committed code
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## v1.6.3

- Implement cooling-related fixes, trying to solve HA Core issue [#132479](https://github.com/home-assistant/core/issues/132479)

## v1.6.2

- Improve control_state processing:
Expand Down
1 change: 1 addition & 0 deletions fixtures/adam_heatpump_cooling/all_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"0ca13e8176204ca7bf6f09de59f81c83": {
"available": true,
"binary_sensors": {
"cooling_enabled": true,
"cooling_state": false,
"dhw_state": true,
"flame_state": false,
Expand Down
1 change: 1 addition & 0 deletions fixtures/adam_onoff_cooling_fake_firmware/all_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"devices": {
"0ca13e8176204ca7bf6f09de59f81c83": {
"binary_sensors": {
"cooling_enabled": true,
"cooling_state": true,
"dhw_state": true,
"flame_state": false,
Expand Down
37 changes: 19 additions & 18 deletions plugwise/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,8 @@ def _get_entity_data(self, entity_id: str) -> GwEntityData:
# Switching groups data
self._entity_switching_group(entity, data)
# Adam data
self._get_adam_data(entity, data)
if self.smile(ADAM):
self._get_adam_data(entity, data)

# Thermostat data for Anna (presets, temperatures etc)
if self.smile(ANNA) and entity["dev_class"] == "thermostat":
Expand All @@ -225,26 +226,26 @@ def _get_adam_data(self, entity: GwEntityData, data: GwEntityData) -> None:
"""Helper-function for _get_entity_data().

Determine Adam heating-status for on-off heating via valves,
available regulations_modes and thermostat control_states.
available regulations_modes and thermostat control_states,
and add missing cooling_enabled when required.
"""
if self.smile(ADAM):
if entity["dev_class"] == "heater_central":
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we should 'match/case' this, but at least just return before the match against gateway (as that wouldn't be processed e.g. there is nothing more to do once heater central is done)?

# Indicate heating_state based on valves being open in case of city-provided heating
if (
entity["dev_class"] == "heater_central"
and self._on_off_device
and isinstance(self._heating_valves(), int)
):
if self._on_off_device and isinstance(self._heating_valves(), int):
data["binary_sensors"]["heating_state"] = self._heating_valves() != 0

# Show the allowed regulation_modes and gateway_modes
if entity["dev_class"] == "gateway":
if self._reg_allowed_modes:
data["regulation_modes"] = self._reg_allowed_modes
self._count += 1
if self._gw_allowed_modes:
data["gateway_modes"] = self._gw_allowed_modes
self._count += 1

# Add cooling_enabled binary_sensor
if "binary_sensors" in data:
if "cooling_enabled" not in data["binary_sensors"] and self._cooling_present:
data["binary_sensors"]["cooling_enabled"] = self._cooling_enabled

# Show the allowed regulation_modes and gateway_modes
if entity["dev_class"] == "gateway":
if self._reg_allowed_modes:
data["regulation_modes"] = self._reg_allowed_modes
self._count += 1
if self._gw_allowed_modes:
data["gateway_modes"] = self._gw_allowed_modes
self._count += 1

def _climate_data(
self,
Expand Down
10 changes: 7 additions & 3 deletions plugwise/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -798,7 +798,9 @@ def _update_elga_cooling(self, data: GwEntityData) -> None:
# Techneco Elga has cooling-capability
self._cooling_present = True
data["model"] = "Generic heater/cooler"
self._cooling_enabled = data["elga_status_code"] in (8, 9)
# Cooling_enabled in xml does NOT show the correct status!
# Setting it specifically:
self._cooling_enabled = data["binary_sensors"]["cooling_enabled"] = data["elga_status_code"] in (8, 9)
data["binary_sensors"]["cooling_state"] = self._cooling_active = (
data["elga_status_code"] == 8
)
Expand All @@ -812,11 +814,13 @@ def _update_elga_cooling(self, data: GwEntityData) -> None:

def _update_loria_cooling(self, data: GwEntityData) -> None:
"""Loria/Thermastage: base cooling-related on cooling_state and modulation_level."""
self._cooling_enabled = data["binary_sensors"]["cooling_state"]
# For Loria/Thermastage it's not clear if cooling_enabled in xml shows the correct status,
# setting it specifically:
self._cooling_enabled = data["binary_sensors"]["cooling_enabled"] = data["binary_sensors"]["cooling_state"]
self._cooling_active = data["sensors"]["modulation_level"] == 100
# For Loria the above does not work (pw-beta issue #301)
if "cooling_ena_switch" in data["switches"]:
self._cooling_enabled = data["switches"]["cooling_ena_switch"]
self._cooling_enabled = data["binary_sensors"]["cooling_enabled"] = data["switches"]["cooling_ena_switch"]
self._cooling_active = data["binary_sensors"]["cooling_state"]

def _cleanup_data(self, data: GwEntityData) -> None:
Expand Down
2 changes: 2 additions & 0 deletions plugwise/smile.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ async def async_update(self) -> PlugwiseData:
try:
await self.full_xml_update()
self.get_all_gateway_entities()
# Set self._cooling_enabled -required for set_temperature,
#also, check for a failed data-retrieval
if "heater_id" in self.gw_data:
heat_cooler = self.gw_entities[self.gw_data["heater_id"]]
if (
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "plugwise"
version = "1.6.2"
version = "1.6.3a0"
bouwew marked this conversation as resolved.
Show resolved Hide resolved
license = {file = "LICENSE"}
description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3."
readme = "README.md"
Expand Down
1 change: 1 addition & 0 deletions tests/data/adam/adam_heatpump_cooling.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"0ca13e8176204ca7bf6f09de59f81c83": {
"available": true,
"binary_sensors": {
"cooling_enabled": true,
"cooling_state": false,
"dhw_state": true,
"flame_state": false,
Expand Down
1 change: 1 addition & 0 deletions tests/data/adam/adam_onoff_cooling_fake_firmware.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"entities": {
"0ca13e8176204ca7bf6f09de59f81c83": {
"binary_sensors": {
"cooling_enabled": true,
"cooling_state": true,
"dhw_state": true,
"flame_state": false,
Expand Down
100 changes: 100 additions & 0 deletions tests/data/anna/anna_elga_2_cooling_UPDATED_DATA.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
{
"entities": {
"573c152e7d4f4720878222bd75638f5b": {
"available": true,
"binary_sensors": {
"compressor_state": true,
"cooling_enabled": false,
"cooling_state": false,
"dhw_state": false,
"flame_state": true,
"heating_state": true,
"secondary_boiler_state": false
},
"dev_class": "heater_central",
"location": "d34dfe6ab90b410c98068e75de3eb631",
"maximum_boiler_temperature": {
"lower_bound": 0.0,
"resolution": 1.0,
"setpoint": 60.0,
"upper_bound": 100.0
},
"model": "Generic heater/cooler",
"name": "OpenTherm",
"sensors": {
"domestic_hot_water_setpoint": 60.0,
"intended_boiler_temperature": 0.0,
"modulation_level": 0.0,
"outdoor_air_temperature": 3.0,
"return_temperature": 23.4,
"water_pressure": 0.5,
"water_temperature": 22.8
},
"switches": {
"dhw_cm_switch": true
},
"vendor": "Techneco"
},
"ebd90df1ab334565b5895f37590ccff4": {
"active_preset": "home",
"available_schedules": [
"Thermostat schedule",
"off"
],
"climate_mode": "auto",
"dev_class": "thermostat",
"firmware": "2018-02-08T11:15:53+01:00",
"hardware": "6539-1301-5002",
"location": "d3ce834534114348be628b61b26d9220",
"model": "ThermoTouch",
"name": "Anna",
"preset_modes": [
"away",
"no_frost",
"vacation",
"home",
"asleep"
],
"select_schedule": "Thermostat schedule",
"sensors": {
"cooling_activation_outdoor_temperature": 26.0,
"cooling_deactivation_threshold": 3.0,
"illuminance": 0.5,
"setpoint_high": 30.0,
"setpoint_low": 19.5,
"temperature": 18.9
},
"temperature_offset": {
"lower_bound": -2.0,
"resolution": 0.1,
"setpoint": 0.0,
"upper_bound": 2.0
},
"thermostat": {
"lower_bound": 4.0,
"resolution": 0.1,
"setpoint_high": 30.0,
"setpoint_low": 19.5,
"upper_bound": 30.0
},
"vendor": "Plugwise"
},
"fb49af122f6e4b0f91267e1cf7666d6f": {
"binary_sensors": {
"plugwise_notification": false
},
"dev_class": "gateway",
"firmware": "4.2.1",
"hardware": "AME Smile 2.0 board",
"location": "d34dfe6ab90b410c98068e75de3eb631",
"mac_address": "C4930002FE76",
"model": "Gateway",
"model_id": "smile_thermo",
"name": "Smile Anna",
"sensors": {
"outdoor_temperature": 3.0
},
"vendor": "Plugwise"
}
}
}
2 changes: 2 additions & 0 deletions tests/test_adam.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,8 @@ async def test_adam_heatpump_cooling(self):
assert smile._last_active["8cf650a4c10c44819e426bed406aec34"] == WERKDAG_SCHEMA
assert smile._last_active["5cc21042f87f4b4c94ccb5537c47a53f"] == WERKDAG_SCHEMA
assert self.entity_items == 497
assert self.cooling_present
assert self._cooling_enabled

await smile.close_connection()
await self.disconnect(server, client)
Expand Down
30 changes: 30 additions & 0 deletions tests/test_anna.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ async def test_connect_anna_heatpump_heating(self):
good_schedules=[
"standaard",
],
fail_cooling=True,
)
_LOGGER.debug(
"ERROR raised setting good schedule standaard: %s", exc.value
Expand Down Expand Up @@ -261,6 +262,7 @@ async def test_connect_anna_heatpump_cooling(self):
good_schedules=[
"standaard",
],
fail_cooling=True,
)
_LOGGER.debug(
"ERROR raised good schedule to standaard: %s", exc.value
Expand Down Expand Up @@ -407,6 +409,33 @@ async def test_connect_anna_elga_2_cooling(self):
assert self._cooling_enabled
assert self._cooling_active

result = await self.tinker_thermostat(
smile,
"d3ce834534114348be628b61b26d9220",
good_schedules=["Thermostat schedule"],
)
assert result

# Simulate a change of season: from cooling to heating after an update_interval
testdata_updated = self.load_testdata(
SMILE_TYPE, f"{self.smile_setup}_UPDATED_DATA"
)

self.smile_setup = "updated/anna_elga_2_switch_heating"
await self.device_test(
smile, "2020-04-05 00:00:01", testdata_updated, initialize=False
)
assert self.cooling_present
assert not self._cooling_enabled
assert not self._cooling_active

result = await self.tinker_thermostat(
smile,
"d3ce834534114348be628b61b26d9220",
good_schedules=["Thermostat schedule"],
)
assert result

await smile.close_connection()
await self.disconnect(server, client)

Expand Down Expand Up @@ -445,6 +474,7 @@ async def test_connect_anna_loria_heating_idle(self):
good_schedules=[
"Winter",
],
fail_cooling=True,
)
_LOGGER.debug(
"ERROR raised setting to schedule Winter: %s", exc.value
Expand Down
16 changes: 12 additions & 4 deletions tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -704,14 +704,20 @@ async def tinker_switch(

@pytest.mark.asyncio
async def tinker_thermostat_temp(
self, smile, loc_id, block_cooling=False, unhappy=False
self, smile, loc_id, block_cooling=False, fail_cooling=False, unhappy=False
):
"""Toggle temperature to test functionality."""
_LOGGER.info("Asserting modifying settings in location (%s):", loc_id)
tinker_temp_passed = False
test_temp = {"setpoint": 22.9}
if self.cooling_present and not block_cooling:
test_temp = {"setpoint_low": 19.5, "setpoint_high": 23.5}
if smile.smile_name == "Smile Anna":
if self._cooling_enabled:
test_temp = {"setpoint_low": 4.0, "setpoint_high": 23.0}
else:
test_temp = {"setpoint_low": 19.0, "setpoint_high": 30.0}
if fail_cooling:
test_temp = {"setpoint_low": 19.0, "setpoint_high": 23.0}
_LOGGER.info("- Adjusting temperature to %s", test_temp)
try:
await smile.set_temperature(loc_id, test_temp)
Expand Down Expand Up @@ -826,14 +832,15 @@ async def tinker_thermostat(
good_schedules=None,
single=False,
block_cooling=False,
fail_cooling=False,
unhappy=False,
):
"""Toggle various climate settings to test functionality."""
if good_schedules is None: # pragma: no cover
good_schedules = ["Weekschema"]

result_1 = await self.tinker_thermostat_temp(
smile, loc_id, block_cooling, unhappy
smile, loc_id, block_cooling, fail_cooling, unhappy
)
result_2 = await self.tinker_thermostat_preset(smile, loc_id, unhappy)
if smile._schedule_old_states != {}:
Expand All @@ -858,11 +865,12 @@ async def tinker_legacy_thermostat(
smile,
schedule_on=True,
block_cooling=False,
fail_cooling=False,
unhappy=False,
):
"""Toggle various climate settings to test functionality."""
result_1 = await self.tinker_thermostat_temp(
smile, "dummy", block_cooling, unhappy
smile, "dummy", block_cooling, fail_cooling, unhappy
)
result_2 = await self.tinker_thermostat_preset(smile, None, unhappy)
result_3 = await self.tinker_legacy_thermostat_schedule(smile, unhappy)
Expand Down
Empty file.
Loading
Loading