Skip to content

Commit 617c1fd

Browse files
authored
Merge pull request #114 from plugwise/cooling_anna
Anna: implement detection of cooling presence
2 parents b8497b9 + 32ae309 commit 617c1fd

File tree

7 files changed

+100
-37
lines changed

7 files changed

+100
-37
lines changed

CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# Changelog
22

3-
# ongoing
4-
- Improved dependency handling (@dependabot)
3+
# v0.15.7 - Smile - Anna: better implementation of cooling-function-detection
4+
- Anna: add two sensors related to automatic switching between heating and cooling and add a heating/cooling mode active indication
5+
- Adam: also provide a heating/cooling mode active indication
6+
- Improved dependency handling (@dependabot)
57

68
# v0.15.6 - Smile - Various fixes and improvements
79
- Adam: collect `control_state` from master thermostats, allows showing the thermostat state as on the Plugwise App

plugwise/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Plugwise module."""
22

3-
__version__ = "0.15.6"
3+
__version__ = "0.15.7a5"
44

55
from plugwise.smile import Smile
66
from plugwise.stick import Stick

plugwise/constants.py

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -397,24 +397,26 @@
397397
# zone_thermosstat 'temperature_offset'
398398
# radiator_valve 'uncorrected_temperature', 'temperature_offset'
399399
DEVICE_MEASUREMENTS = {
400-
# HA Core current_temperature
400+
# HA Core thermostat current_temperature
401401
"temperature": {ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS},
402-
# HA Core setpoint
402+
# HA Core thermostat setpoint
403403
"thermostat": {ATTR_NAME: "setpoint", ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS},
404+
# Specific for an Anna
404405
"illuminance": {ATTR_UNIT_OF_MEASUREMENT: UNIT_LUMEN},
405-
"outdoor_temperature": {
406-
ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS
407-
}, # Outdoor temp as reported on the Anna, in the App
408-
"schedule_temperature": {
409-
ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS
410-
}, # Only present on legacy Anna and Anna_v3
411-
# Lisa and Tom
406+
# Outdoor temperature from APPLIANCES - present for a heatpump
407+
"outdoor_temperature": {ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS},
408+
# Schedule temperature - only present for a legacy Anna or an Anna v3
409+
"schedule_temperature": {ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS},
410+
# Specific for an Anna with heatpump extension installed
411+
"cooling_activation_outdoor_temperature": {ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS},
412+
"cooling_deactivation_threshold": {ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS},
413+
# Specific for a Lisa a Tom/Floor
412414
"battery": {ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE},
413415
"temperature_difference": {ATTR_UNIT_OF_MEASUREMENT: DEGREE},
414416
"valve_position": {ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE},
415-
# Jip
417+
# Specific for a Jip
416418
"humidity": {ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE},
417-
# Plug
419+
# Specific for a Plug
418420
"electricity_consumed": {ATTR_UNIT_OF_MEASUREMENT: POWER_WATT},
419421
"electricity_produced": {ATTR_UNIT_OF_MEASUREMENT: POWER_WATT},
420422
"relay": {ATTR_UNIT_OF_MEASUREMENT: None},
@@ -435,15 +437,15 @@
435437
},
436438
"intended_boiler_temperature": {
437439
ATTR_UNIT_OF_MEASUREMENT: TEMP_CELSIUS
438-
}, # non-zero when heating, zero when dhw-heating
440+
}, # Non-zero when heating, zero when dhw-heating
439441
"central_heating_state": {
440442
ATTR_NAME: "c_heating_state",
441443
ATTR_UNIT_OF_MEASUREMENT: None,
442-
}, # for Elga (heatpump) use this instead of intended_central_heating_state
444+
}, # For Elga (heatpump) use this instead of intended_central_heating_state
443445
"intended_central_heating_state": {
444446
ATTR_NAME: "heating_state",
445447
ATTR_UNIT_OF_MEASUREMENT: None,
446-
}, # use intended_c_h_state, this key shows the heating-behavior better than c-h_state
448+
}, # This key shows in general the heating-behavior better than c-h_state. except when connected to a heatpump
447449
"modulation_level": {ATTR_UNIT_OF_MEASUREMENT: PERCENTAGE},
448450
"return_water_temperature": {
449451
ATTR_NAME: "return_temperature",
@@ -456,15 +458,14 @@
456458
"slave_boiler_state": {ATTR_UNIT_OF_MEASUREMENT: None},
457459
"flame_state": {
458460
ATTR_UNIT_OF_MEASUREMENT: None
459-
}, # also present when there is a single gas-heater
460-
# Anna only
461+
}, # Also present when there is a single gas-heater
461462
"central_heater_water_pressure": {
462463
ATTR_NAME: "water_pressure",
463464
ATTR_UNIT_OF_MEASUREMENT: PRESSURE_BAR,
464465
},
465466
# Legacy Anna: similar to flame-state on Anna/Adam
466467
"boiler_state": {ATTR_UNIT_OF_MEASUREMENT: None},
467-
# Legacy Anna: shows when heating is active, don't show dhw_state, cannot be determined reliably
468+
# Legacy Anna: shows when heating is active, we don't show dhw_state, cannot be determined reliably
468469
"intended_boiler_state": {ATTR_UNIT_OF_MEASUREMENT: None},
469470
}
470471

@@ -553,6 +554,14 @@
553554
ATTR_ID: "battery",
554555
ATTR_STATE: None,
555556
}
557+
COOL_ACT_THRESHOLD = {
558+
ATTR_ID: "cooling_activation_outdoor_temperature",
559+
ATTR_STATE: None,
560+
}
561+
COOL_DEACT_THRESHOLD = {
562+
ATTR_ID: "cooling_deactivation_threshold",
563+
ATTR_STATE: None,
564+
}
556565
CURRENT_TEMP = {
557566
ATTR_ID: "temperature",
558567
ATTR_STATE: None,
@@ -695,6 +704,8 @@
695704
}
696705
SENSORS = [
697706
BATTERY,
707+
COOL_ACT_THRESHOLD,
708+
COOL_DEACT_THRESHOLD,
698709
CURRENT_TEMP,
699710
DEVICE_STATE,
700711
EL_CONSUMED,

plugwise/helper.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -445,14 +445,17 @@ def _appliance_info_finder(self, appliance, appl):
445445
appl.model = appl.name = self.smile_name
446446
appl.v_name = "Plugwise B.V."
447447

448-
# Adam: check for cooling capability, assume heating capability is always present
448+
# Adam: check for cooling capability and active heating/cooling operation-mode
449449
mode_list = []
450-
locator = "./actuator_functionalities/regulation_mode_control_functionality/allowed_modes"
451-
if appliance.find(locator) is not None:
452-
self._cooling_present = False
453-
for mode in appliance.find(locator):
454-
mode_list.append(mode.text)
455-
self._cooling_present = "cooling" in mode_list
450+
locator = "./actuator_functionalities/regulation_mode_control_functionality"
451+
search = appliance.find(locator)
452+
if search is not None:
453+
if search.find("mode") is not None:
454+
self.cooling_active = search.find("mode").text == "cooling"
455+
if search.find("allowed_modes") is not None:
456+
for mode in search.find("allowed_modes"):
457+
mode_list.append(mode.text)
458+
self._cooling_present = "cooling" in mode_list
456459

457460
return appl
458461

@@ -463,6 +466,7 @@ def _appliance_info_finder(self, appliance, appl):
463466
appl.v_name = module_data[0]
464467
appl.model = check_model(module_data[1], appl.v_name)
465468
appl.fw = module_data[3]
469+
466470
return appl
467471

468472
if appl.pwclass == "heater_central":
@@ -741,6 +745,11 @@ def _get_appliance_data(self, d_id):
741745
if "temperature" in data:
742746
data.pop("heating_state", None)
743747

748+
# Anna: check for cooling capability
749+
if "cooling_activation_outdoor_temperature" in data:
750+
data["cooling_active"] = False
751+
self._cooling_present = True
752+
744753
return data
745754

746755
def _rank_thermostat(

plugwise/smile.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,22 @@ def _device_data_anna(self, dev_id, details, device_data):
149149
device_data.pop("boiler_state", None)
150150
device_data.pop("intended_boiler_state", None)
151151

152+
# Anna: indicate possible active heating/cooling operation-mode
153+
# Actual ongoing heating/cooling is shown via heating_state/cooling_state
154+
if "cooling_activation_outdoor_temperature" in device_data:
155+
if (
156+
not self.cooling_active
157+
and device_data["temperature"]
158+
> device_data["cooling_activation_outdoor_temperature"]
159+
):
160+
device_data["cooling_active"] = self.cooling_active = True
161+
if (
162+
self.cooling_active
163+
and device_data["temperature"]
164+
< device_data["cooling_deactivation_threshold"]
165+
):
166+
device_data["cooling_active"] = self.cooling_active = False
167+
152168
return device_data
153169

154170
def _device_data_adam(self, details, device_data):
@@ -166,6 +182,11 @@ def _device_data_adam(self, details, device_data):
166182
if self._heating_valves() == 0:
167183
device_data["heating_state"] = False
168184

185+
# Adam: indicate active heating/cooling operation-mode
186+
# Actual ongoing heating/cooling is shown via heating_state/cooling_state
187+
if details["class"] == "heater_central":
188+
device_data["cooling_active"] = self.cooling_active
189+
169190
return device_data
170191

171192
def _device_data_climate(self, details, device_data):
@@ -185,7 +206,7 @@ def _device_data_climate(self, details, device_data):
185206
else:
186207
device_data["last_used"] = self._last_active_schema(details["location"])
187208

188-
# Find the thermostat control_state of a location, from domain_objects
209+
# Find the thermostat control_state of a location, from DOMAIN_OBJECTS
189210
# The control_state represents the heating/cooling demand-state of the master thermostat
190211
# Note: heating or cooling can still be active when the setpoint has been reached
191212
locator = f'location[@id="{details["location"]}"]'
@@ -287,7 +308,7 @@ def __init__(
287308
self._active_device_present = None
288309
self._appliances = None
289310
self._appl_data = None
290-
self._cooling_present = None
311+
self._cooling_present = False
291312
self._domain_objects = None
292313
self._heater_id = None
293314
self._home_location = None
@@ -300,6 +321,7 @@ def __init__(
300321
self._stretch_v3 = False
301322
self._thermo_locs = None
302323

324+
self.cooling_active = False
303325
self.gateway_id = None
304326
self.gw_data = {}
305327
self.gw_devices = {}

tests/test_smile.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -365,14 +365,19 @@ def show_setup(location_list, device_list):
365365
_LOGGER.info(" ! no devices found in this location")
366366

367367
@pytest.mark.asyncio
368-
async def device_test(self, smile=pw_smile.Smile, testdata=None):
368+
async def device_test(self, smile=pw_smile.Smile, testdata=None, preset=False):
369369
"""Perform basic device tests."""
370370
_LOGGER.info("Asserting testdata:")
371371
bsw_list = ["binary_sensors", "sensors", "switches"]
372372
smile.get_all_devices()
373+
# Preset smile.cooling_active for testing of a state-change
374+
smile.cooling_active = False
375+
if preset:
376+
smile.cooling_active = True
373377
data = await smile.async_update()
374378
extra = data[0]
375379
device_list = data[1]
380+
376381
self.active_device_present = extra["active_device"]
377382
self.cooling_present = extra["cooling_present"]
378383
self.notifications = extra["notifications"]
@@ -1085,6 +1090,7 @@ async def test_connect_adam_plus_anna_new(self):
10851090
"ee62cad889f94e8ca3d09021f03a660b": {"control_state": "off"},
10861091
# Central
10871092
"2743216f626f43948deec1f7ab3b3d70": {
1093+
"cooling_active": False,
10881094
"binary_sensors": [{"id": "dhw_state", "state": True}],
10891095
"sensors": [{"id": "device_state", "state": "dhw-heating"}],
10901096
},
@@ -1108,7 +1114,6 @@ async def test_connect_adam_plus_anna_new(self):
11081114
_LOGGER.info(" # Assert master thermostat")
11091115
assert not smile._sm_thermostat
11101116
assert self.active_device_present
1111-
assert not self.cooling_present
11121117

11131118
switch_change = await self.tinker_switch(
11141119
smile,
@@ -1473,7 +1478,7 @@ async def test_connect_p1v3(self):
14731478

14741479
await self.device_test(smile, testdata)
14751480
assert smile._sm_thermostat is None # it's not a thermostat :)
1476-
assert self.cooling_present is None # not a heating/cooling system
1481+
assert not self.cooling_present
14771482
assert not self.notifications
14781483

14791484
await smile.close_connection()
@@ -1565,9 +1570,14 @@ async def test_connect_anna_heatpump(self):
15651570
"3cb70739631c4d17a86b8b12e8a5161b": {
15661571
"selected_schedule": "standaard",
15671572
"active_preset": "home",
1568-
"sensors": [{"id": "illuminance", "state": 86.0}],
1573+
"cooling_active": False,
1574+
"sensors": [
1575+
{"id": "illuminance", "state": 86.0},
1576+
{"id": "cooling_activation_outdoor_temperature", "state": 21.0},
1577+
{"id": "cooling_deactivation_threshold", "state": 4},
1578+
],
15691579
},
1570-
# Central
1580+
# Heater central
15711581
"1cbf783bb11e4a7c8a6843dee3a86927": {
15721582
"cooling_state": False,
15731583
"heating_state": True,
@@ -1578,10 +1588,12 @@ async def test_connect_anna_heatpump(self):
15781588
}
15791589
],
15801590
"sensors": [
1591+
{"id": "outdoor_temperature", "state": 18.0},
15811592
{"id": "water_temperature", "state": 29.1},
15821593
{"id": "water_pressure", "state": 1.57},
15831594
],
15841595
},
1596+
# Gateway
15851597
"015ae9ea3f964e668e490fa39da3870b": {
15861598
"sensors": [{"id": "outdoor_temperature", "state": 20.2}]
15871599
},
@@ -1599,10 +1611,11 @@ async def test_connect_anna_heatpump(self):
15991611
_LOGGER.info(" # Assert no legacy")
16001612
assert not smile._smile_legacy # pylint: disable=protected-access
16011613

1602-
await self.device_test(smile, testdata)
1614+
await self.device_test(smile, testdata, True)
16031615
_LOGGER.info(" # Assert master thermostat")
16041616
assert smile._sm_thermostat
16051617
assert self.active_device_present
1618+
assert self.cooling_present
16061619
assert not self.notifications
16071620

16081621
await smile.close_connection()
@@ -1617,7 +1630,12 @@ async def test_connect_anna_heatpump_cooling(self):
16171630
"3cb70739631c4d17a86b8b12e8a5161b": {
16181631
"selected_schedule": None,
16191632
"active_preset": "home",
1620-
"sensors": [{"id": "illuminance", "state": 25.5}],
1633+
"cooling_active": True,
1634+
"sensors": [
1635+
{"id": "illuminance", "state": 25.5},
1636+
{"id": "cooling_activation_outdoor_temperature", "state": 21.0},
1637+
{"id": "cooling_deactivation_threshold", "state": 6},
1638+
],
16211639
},
16221640
# Central
16231641
"1cbf783bb11e4a7c8a6843dee3a86927": {
@@ -1655,6 +1673,7 @@ async def test_connect_anna_heatpump_cooling(self):
16551673
_LOGGER.info(" # Assert master thermostat")
16561674
assert smile._sm_thermostat
16571675
assert self.active_device_present
1676+
assert self.cooling_present
16581677
assert not self.notifications
16591678

16601679
await smile.close_connection()

userdata/anna_heatpump/core.appliances.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1015,7 +1015,7 @@
10151015
<unit>C</unit>
10161016
<last_consecutive_log_date>2020-04-09T14:12:19.497+02:00</last_consecutive_log_date>
10171017
<period start_date="2020-04-09T14:12:19.497+02:00" end_date="2020-04-09T14:12:19.497+02:00">
1018-
<measurement log_date="2020-04-09T14:12:19.497+02:00">23.30</measurement>
1018+
<measurement log_date="2020-04-09T14:12:19.497+02:00">3.0</measurement>
10191019
</period>
10201020
<thermo_meter id='0961c99850a942319310d93d2074127b'/>
10211021
</point_log>

0 commit comments

Comments
 (0)