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

Fix for beta issue 574 #487

Merged
merged 15 commits into from
Dec 29, 2023
Merged
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Changelog

## Ongoing
## v0.36.2

- Improve support for Anna+Elga systems that do not support cooling (fix for [#547](https://github.com/plugwise/plugwise-beta/issues/547)).
- Update test-fixtures for Plugwise-beta/Core Plugwise.
- Fix deprecation-warnings.

Expand Down
100 changes: 100 additions & 0 deletions fixtures/anna_elga_no_cooling/all_data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
{
"devices": {
"015ae9ea3f964e668e490fa39da3870b": {
"binary_sensors": {
"plugwise_notification": false
},
"dev_class": "gateway",
"firmware": "4.0.15",
"hardware": "AME Smile 2.0 board",
"location": "a57efe5f145f498c9be62a9b63626fbf",
"mac_address": "012345670001",
"model": "Gateway",
"name": "Smile Anna",
"sensors": {
"outdoor_temperature": 20.2
},
"vendor": "Plugwise"
},
"1cbf783bb11e4a7c8a6843dee3a86927": {
"available": true,
"binary_sensors": {
"compressor_state": true,
"dhw_state": false,
"flame_state": false,
"heating_state": true,
"slave_boiler_state": false
},
"dev_class": "heater_central",
"location": "a57efe5f145f498c9be62a9b63626fbf",
"max_dhw_temperature": {
"lower_bound": 35.0,
"resolution": 0.01,
"setpoint": 53.0,
"upper_bound": 60.0
},
"maximum_boiler_temperature": {
"lower_bound": 0.0,
"resolution": 1.0,
"setpoint": 60.0,
"upper_bound": 100.0
},
"model": "Generic heater",
"name": "OpenTherm",
"sensors": {
"dhw_temperature": 46.3,
"intended_boiler_temperature": 35.0,
"modulation_level": 52,
"outdoor_air_temperature": 3.0,
"return_temperature": 25.1,
"water_pressure": 1.57,
"water_temperature": 29.1
},
"switches": {
"dhw_cm_switch": false
},
"vendor": "Techneco"
},
"3cb70739631c4d17a86b8b12e8a5161b": {
"active_preset": "home",
"available_schedules": ["standaard", "off"],
"dev_class": "thermostat",
"firmware": "2018-02-08T11:15:53+01:00",
"hardware": "6539-1301-5002",
"location": "c784ee9fdab44e1395b8dee7d7a497d5",
"mode": "auto",
"model": "ThermoTouch",
"name": "Anna",
"preset_modes": ["no_frost", "home", "away", "asleep", "vacation"],
"select_schedule": "standaard",
"sensors": {
"cooling_activation_outdoor_temperature": 21.0,
"cooling_deactivation_threshold": 4.0,
"illuminance": 86.0,
"setpoint": 20.5,
"temperature": 19.3
},
"temperature_offset": {
"lower_bound": -2.0,
"resolution": 0.1,
"setpoint": -0.5,
"upper_bound": 2.0
},
"thermostat": {
"lower_bound": 4.0,
"resolution": 0.1,
"setpoint": 20.5,
"upper_bound": 30.0
},
"vendor": "Plugwise"
}
},
"gateway": {
"cooling_present": false,
"gateway_id": "015ae9ea3f964e668e490fa39da3870b",
"heater_id": "1cbf783bb11e4a7c8a6843dee3a86927",
"item_count": 62,
"notifications": {},
"smile_name": "Smile Anna"
}
}
5 changes: 5 additions & 0 deletions fixtures/anna_elga_no_cooling/device_list.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
"015ae9ea3f964e668e490fa39da3870b",
"1cbf783bb11e4a7c8a6843dee3a86927",
"3cb70739631c4d17a86b8b12e8a5161b"
]
1 change: 1 addition & 0 deletions fixtures/anna_elga_no_cooling/notifications.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
9 changes: 9 additions & 0 deletions plugwise/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@
# Used with the Elga heatpump - marcelveldt
"compressor_state": UOM(NONE),
"cooling_state": UOM(NONE),
"thermostat_supports_cooling": UOM(NONE),
# Available with the Loria and Elga (newer Anna firmware) heatpumps
"cooling_enabled": UOM(NONE),
# Next 2 keys are used to show the state of the gas-heater used next to the Elga heatpump - marcelveldt
Expand Down Expand Up @@ -332,6 +333,13 @@
"heater_electric",
)

SpecialType = Literal[
"c_heating_state",
"thermostat_supports_cooling",
]

SPECIALS: Final[tuple[str, ...]] = get_args(SpecialType)

SPECIAL_FORMAT: Final[tuple[str, ...]] = (ENERGY_KILO_WATT_HOUR, VOLUME_CUBIC_METERS)

SwitchType = Literal[
Expand Down Expand Up @@ -510,6 +518,7 @@ class DeviceData(TypedDict, total=False):
domestic_hot_water_setpoint: float
elga_status_code: int
c_heating_state: bool
thermostat_supports_cooling: bool

# Device availability
available: bool | None
Expand Down
41 changes: 25 additions & 16 deletions plugwise/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
POWER_WATT,
SENSORS,
SPECIAL_PLUG_TYPES,
SPECIALS,
SWITCH_GROUP_TYPES,
SWITCHES,
TEMP_CELSIUS,
Expand All @@ -61,6 +62,7 @@
GatewayData,
ModelData,
SensorType,
SpecialType,
SwitchType,
ThermoLoc,
ToggleNameType,
Expand Down Expand Up @@ -860,9 +862,10 @@ def _appliance_measurements(
sw_key = cast(SwitchType, measurement)
sw_value = appl_p_loc.text in ["on", "true"]
data["switches"][sw_key] = sw_value
case "c_heating_state":
value = appl_p_loc.text in ["on", "true"]
data["c_heating_state"] = value
case _ as measurement if measurement in SPECIALS:
sp_key = cast(SpecialType, measurement)
sp_value = appl_p_loc.text in ["on", "true"]
data[sp_key] = sp_value
case "elga_status_code":
data["elga_status_code"] = int(appl_p_loc.text)

Expand Down Expand Up @@ -1003,10 +1006,14 @@ def _cleanup_data(self, data: DeviceData) -> None:
if "cooling_ena_switch" in data["switches"]:
data["switches"].pop("cooling_ena_switch") # pragma: no cover
self._count -= 1 # pragma: no cover
if not self._elga and "cooling_enabled" in data:
data.pop("cooling_enabled") # pragma: no cover
if "cooling_enabled" in data["binary_sensors"]:
data["binary_sensors"].pop("cooling_enabled") # pragma: no cover
self._count -= 1 # pragma: no cover

if "thermostat_supports_cooling" in data:
data.pop("thermostat_supports_cooling", None)
self._count -= 1

def _process_c_heating_state(self, data: DeviceData) -> None:
"""Helper-function for _get_measurement_data().

Expand Down Expand Up @@ -1100,19 +1107,21 @@ def _get_measurement_data(self, dev_id: str) -> DeviceData:
if self._is_thermostat and self.smile(ANNA) and dev_id == self._heater_id:
# Anna+Elga: base cooling_state on the elga-status-code
if "elga_status_code" in data:
# Techneco Elga has cooling-capability
self._cooling_present = True
data["model"] = "Generic heater/cooler"
self._cooling_enabled = data["elga_status_code"] in [8, 9]
data["binary_sensors"]["cooling_state"] = self._cooling_active = (
data["elga_status_code"] == 8
)
if data["thermostat_supports_cooling"]:
# Techneco Elga has cooling-capability
self._cooling_present = True
data["model"] = "Generic heater/cooler"
self._cooling_enabled = data["elga_status_code"] in [8, 9]
data["binary_sensors"]["cooling_state"] = self._cooling_active = (
data["elga_status_code"] == 8
)
# Elga has no cooling-switch
if "cooling_ena_switch" in data["switches"]:
data["switches"].pop("cooling_ena_switch")
self._count -= 1

data.pop("elga_status_code", None)
self._count -= 1
# Elga has no cooling-switch
if "cooling_ena_switch" in data["switches"]:
data["switches"].pop("cooling_ena_switch")
self._count -= 1

# Loria/Thermastage: cooling-related is based on cooling_state
# and modulation_level
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 = "0.36.1"
version = "0.36.2"
license = {file = "LICENSE"}
description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3."
readme = "README.md"
Expand Down
90 changes: 90 additions & 0 deletions tests/data/anna/anna_elga_no_cooling.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
{
"015ae9ea3f964e668e490fa39da3870b": {
"dev_class": "gateway",
"firmware": "4.0.15",
"hardware": "AME Smile 2.0 board",
"location": "a57efe5f145f498c9be62a9b63626fbf",
"mac_address": "012345670001",
"model": "Gateway",
"name": "Smile Anna",
"vendor": "Plugwise",
"binary_sensors": {
"plugwise_notification": false
},
"sensors": {
"outdoor_temperature": 20.2
}
},
"1cbf783bb11e4a7c8a6843dee3a86927": {
"dev_class": "heater_central",
"location": "a57efe5f145f498c9be62a9b63626fbf",
"model": "Generic heater",
"name": "OpenTherm",
"vendor": "Techneco",
"maximum_boiler_temperature": {
"setpoint": 60,
"lower_bound": 0,
"upper_bound": 100,
"resolution": 1
},
"max_dhw_temperature": {
"setpoint": 53,
"lower_bound": 35,
"upper_bound": 60,
"resolution": 0.01
},
"available": true,
"binary_sensors": {
"dhw_state": false,
"heating_state": true,
"compressor_state": true,
"slave_boiler_state": false,
"flame_state": false
},
"sensors": {
"water_temperature": 29.1,
"dhw_temperature": 46.3,
"intended_boiler_temperature": 35,
"modulation_level": 52,
"return_temperature": 25.1,
"water_pressure": 1.57,
"outdoor_air_temperature": 3
},
"switches": {
"dhw_cm_switch": false
}
},
"3cb70739631c4d17a86b8b12e8a5161b": {
"dev_class": "thermostat",
"firmware": "2018-02-08T11:15:53+01:00",
"hardware": "6539-1301-5002",
"location": "c784ee9fdab44e1395b8dee7d7a497d5",
"model": "ThermoTouch",
"name": "Anna",
"vendor": "Plugwise",
"temperature_offset": {
"lower_bound": -2,
"resolution": 0.1,
"upper_bound": 2,
"setpoint": -0.5
},
"thermostat": {
"setpoint": 20.5,
"lower_bound": 4,
"upper_bound": 30,
"resolution": 0.1
},
"preset_modes": ["no_frost", "home", "away", "asleep", "vacation"],
"active_preset": "home",
"available_schedules": ["standaard", "off"],
"select_schedule": "standaard",
"mode": "auto",
"sensors": {
"setpoint": 20.5,
"temperature": 19.3,
"illuminance": 86,
"cooling_activation_outdoor_temperature": 21,
"cooling_deactivation_threshold": 4
}
}
}
26 changes: 26 additions & 0 deletions tests/test_anna.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,32 @@ async def test_connect_anna_heatpump_cooling_fake_firmware(self):
await smile.close_connection()
await self.disconnect(server, client)

@pytest.mark.asyncio
async def test_connect_anna_elga_no_cooling(self):
"""Test an Anna with Elga, cooling-mode not used, in heating mode."""

self.smile_setup = "anna_elga_no_cooling"

testdata = self.load_testdata(SMILE_TYPE, self.smile_setup)
server, smile, client = await self.connect_wrapper()
assert smile.smile_hostname == "smile000000"

self.validate_test_basics(
_LOGGER,
smile,
smile_version="4.0.15",
)

await self.device_test(smile, "2020-04-12 00:00:01", testdata)
assert smile.gateway_id == "015ae9ea3f964e668e490fa39da3870b"
assert smile._last_active["c784ee9fdab44e1395b8dee7d7a497d5"] == "standaard"
assert smile.device_items == 62
assert not self.notifications
assert not self.cooling_present

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

@pytest.mark.asyncio
async def test_connect_anna_elga_2(self):
"""Test a 2nd Anna with Elga setup, cooling off, in idle mode (with missing outdoor temperature - solved)."""
Expand Down
Loading
Loading