Skip to content

Commit b60c336

Browse files
authored
Merge pull request #16 from RaHehl/feature/atw-extended-properties
feat(atw): add extended ATW properties for Ecodan heat pumps
2 parents 004b804 + fa11967 commit b60c336

4 files changed

Lines changed: 342 additions & 18 deletions

File tree

src/pymelcloud/atw_device.py

Lines changed: 167 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
"""Air-To-Water (DeviceType=1) device definition."""
2+
3+
import warnings
24
from typing import Any, Callable, Dict, List, Optional
35

46
from pymelcloud.device import EFFECTIVE_FLAGS, Device
@@ -9,11 +11,15 @@
911
PROPERTY_ZONE_2_TARGET_TEMPERATURE = "zone_2_target_temperature"
1012
PROPERTY_ZONE_1_TARGET_HEAT_FLOW_TEMPERATURE = "zone_1_target_heat_flow_temperature"
1113
PROPERTY_ZONE_2_TARGET_HEAT_FLOW_TEMPERATURE = "zone_2_target_heat_flow_temperature"
12-
PROPERTY_ZONE_1_TARGET_COOL_FLOW_TEMPERATURE = "zone_1_target_heat_cool_temperature"
13-
PROPERTY_ZONE_2_TARGET_COOL_FLOW_TEMPERATURE = "zone_2_target_heat_cool_temperature"
14+
PROPERTY_ZONE_1_TARGET_COOL_FLOW_TEMPERATURE = "zone_1_target_cool_flow_temperature"
15+
PROPERTY_ZONE_2_TARGET_COOL_FLOW_TEMPERATURE = "zone_2_target_cool_flow_temperature"
1416
PROPERTY_ZONE_1_OPERATION_MODE = "zone_1_operation_mode"
1517
PROPERTY_ZONE_2_OPERATION_MODE = "zone_2_operation_mode"
1618

19+
# Deprecated aliases for backwards compatibility
20+
PROPERTY_ZONE_1_TARGET_HEAT_COOL_TEMPERATURE = "zone_1_target_heat_cool_temperature"
21+
PROPERTY_ZONE_2_TARGET_HEAT_COOL_TEMPERATURE = "zone_2_target_heat_cool_temperature"
22+
1723
OPERATION_MODE_AUTO = "auto"
1824
OPERATION_MODE_FORCE_HOT_WATER = "force_hot_water"
1925

@@ -155,22 +161,52 @@ async def set_target_temperature(self, target_temperature):
155161
await self._device.set({prop: target_temperature})
156162

157163
@property
158-
def flow_temperature(self) -> float:
164+
def flow_temperature(self) -> float | None:
159165
"""Return current flow temperature.
160166
161-
This value is not available in the standard state poll response. The poll
162-
update frequency can be a little bit lower that expected.
167+
.. deprecated::
168+
Use `device.flow_temperature` or `zone.zone_flow_temperature` instead.
163169
"""
164-
return self._device_conf()["Device"]["FlowTemperature"]
170+
warnings.warn(
171+
"Zone.flow_temperature is deprecated, use device.flow_temperature "
172+
"or zone.zone_flow_temperature instead",
173+
DeprecationWarning,
174+
stacklevel=2,
175+
)
176+
return self._device_conf().get("Device", {}).get("FlowTemperature")
165177

166178
@property
167-
def return_temperature(self) -> float:
168-
"""Return current return flow temperature.
179+
def return_temperature(self) -> float | None:
180+
"""Return current return temperature.
169181
170-
This value is not available in the standard state poll response. The poll
171-
update frequency can be a little bit lower that expected.
182+
.. deprecated::
183+
Use `device.return_temperature` or `zone.zone_return_temperature` instead.
172184
"""
173-
return self._device_conf()["Device"]["ReturnTemperature"]
185+
warnings.warn(
186+
"Zone.return_temperature is deprecated, use device.return_temperature "
187+
"or zone.zone_return_temperature instead",
188+
DeprecationWarning,
189+
stacklevel=2,
190+
)
191+
return self._device_conf().get("Device", {}).get("ReturnTemperature")
192+
193+
@property
194+
def zone_flow_temperature(self) -> float | None:
195+
"""Return current zone-specific flow temperature."""
196+
return (
197+
self._device_conf()
198+
.get("Device", {})
199+
.get(f"FlowTemperatureZone{self.zone_index}")
200+
)
201+
202+
@property
203+
def zone_return_temperature(self) -> float | None:
204+
"""Return current zone-specific return temperature."""
205+
return (
206+
self._device_conf()
207+
.get("Device", {})
208+
.get(f"ReturnTemperatureZone{self.zone_index}")
209+
)
174210

175211
@property
176212
def target_flow_temperature(self) -> Optional[float]:
@@ -309,13 +345,19 @@ def apply_write(self, state: Dict[str, Any], key: str, value: Any):
309345
elif key == PROPERTY_ZONE_1_TARGET_HEAT_FLOW_TEMPERATURE:
310346
state["SetHeatFlowTemperatureZone1"] = self.round_temperature(value)
311347
flags |= 0x1000000000000
312-
elif key == PROPERTY_ZONE_1_TARGET_COOL_FLOW_TEMPERATURE:
348+
elif key in (
349+
PROPERTY_ZONE_1_TARGET_COOL_FLOW_TEMPERATURE,
350+
PROPERTY_ZONE_1_TARGET_HEAT_COOL_TEMPERATURE,
351+
):
313352
state["SetCoolFlowTemperatureZone1"] = self.round_temperature(value)
314353
flags |= 0x1000000000000
315354
elif key == PROPERTY_ZONE_2_TARGET_HEAT_FLOW_TEMPERATURE:
316355
state["SetHeatFlowTemperatureZone2"] = self.round_temperature(value)
317356
flags |= 0x1000000000000
318-
elif key == PROPERTY_ZONE_2_TARGET_COOL_FLOW_TEMPERATURE:
357+
elif key in (
358+
PROPERTY_ZONE_2_TARGET_COOL_FLOW_TEMPERATURE,
359+
PROPERTY_ZONE_2_TARGET_HEAT_COOL_TEMPERATURE,
360+
):
319361
state["SetCoolFlowTemperatureZone2"] = self.round_temperature(value)
320362
flags |= 0x1000000000000
321363
elif key == PROPERTY_ZONE_1_OPERATION_MODE:
@@ -366,21 +408,131 @@ def outside_temperature(self) -> Optional[float]:
366408
"""
367409
return self.get_state_prop("OutdoorTemperature")
368410

411+
@property
412+
def flow_temperature(self) -> Optional[float]:
413+
"""Return current flow temperature of the entire system."""
414+
return self.get_device_prop("FlowTemperature")
415+
416+
@property
417+
def return_temperature(self) -> Optional[float]:
418+
"""Return current return temperature of the entire system."""
419+
return self.get_device_prop("ReturnTemperature")
420+
369421
@property
370422
def flow_temperature_boiler(self) -> Optional[float]:
371423
"""Return flow temperature of the boiler."""
372424
return self.get_device_prop("FlowTemperatureBoiler")
373425

374426
@property
375427
def return_temperature_boiler(self) -> Optional[float]:
376-
"""Return flow temperature of the boiler."""
377-
return self.get_device_prop("FlowTemperatureBoiler")
428+
"""Return the boiler return temperature."""
429+
return self.get_device_prop("ReturnTemperatureBoiler")
378430

379431
@property
380432
def mixing_tank_temperature(self) -> Optional[float]:
381433
"""Return mixing tank temperature."""
382434
return self.get_device_prop("MixingTankWaterTemperature")
383435

436+
@property
437+
def condensing_temperature(self) -> Optional[float]:
438+
"""Return condensing temperature."""
439+
return self.get_device_prop("CondensingTemperature")
440+
441+
@property
442+
def heat_pump_frequency(self) -> Optional[int]:
443+
"""Return current heat pump compressor frequency in Hz."""
444+
return self.get_device_prop("HeatPumpFrequency")
445+
446+
@property
447+
def demand_percentage(self) -> Optional[int]:
448+
"""Return demand percentage (0-100)."""
449+
return self.get_state_prop("DemandPercentage")
450+
451+
@property
452+
def boiler_status(self) -> Optional[bool]:
453+
"""Return boiler status."""
454+
return self.get_device_prop("BoilerStatus")
455+
456+
@property
457+
def booster_heater1_status(self) -> Optional[bool]:
458+
"""Return booster heater 1 status."""
459+
return self.get_device_prop("BoosterHeater1Status")
460+
461+
@property
462+
def booster_heater2_status(self) -> Optional[bool]:
463+
"""Return booster heater 2 status."""
464+
return self.get_device_prop("BoosterHeater2Status")
465+
466+
@property
467+
def booster_heater2plus_status(self) -> Optional[bool]:
468+
"""Return booster heater 2+ status."""
469+
return self.get_device_prop("BoosterHeater2PlusStatus")
470+
471+
@property
472+
def immersion_heater_status(self) -> Optional[bool]:
473+
"""Return immersion heater status."""
474+
return self.get_device_prop("ImmersionHeaterStatus")
475+
476+
@property
477+
def water_pump1_status(self) -> Optional[bool]:
478+
"""Return water pump 1 status."""
479+
return self.get_device_prop("WaterPump1Status")
480+
481+
@property
482+
def water_pump2_status(self) -> Optional[bool]:
483+
"""Return water pump 2 status."""
484+
return self.get_device_prop("WaterPump2Status")
485+
486+
@property
487+
def water_pump3_status(self) -> Optional[bool]:
488+
"""Return water pump 3 status."""
489+
return self.get_device_prop("WaterPump3Status")
490+
491+
@property
492+
def water_pump4_status(self) -> Optional[bool]:
493+
"""Return water pump 4 status."""
494+
return self.get_device_prop("WaterPump4Status")
495+
496+
@property
497+
def valve_3way_status(self) -> Optional[bool]:
498+
"""Return 3-way valve status."""
499+
return self.get_device_prop("ValveStatus3Way")
500+
501+
@property
502+
def valve_2way_status(self) -> Optional[bool]:
503+
"""Return 2-way valve status."""
504+
return self.get_device_prop("ValveStatus2Way")
505+
506+
@property
507+
def daily_heating_energy_consumed(self) -> Optional[float]:
508+
"""Return today's heating energy consumed in kWh."""
509+
return self.get_device_prop("DailyHeatingEnergyConsumed")
510+
511+
@property
512+
def daily_cooling_energy_consumed(self) -> Optional[float]:
513+
"""Return today's cooling energy consumed in kWh."""
514+
return self.get_device_prop("DailyCoolingEnergyConsumed")
515+
516+
@property
517+
def daily_hot_water_energy_consumed(self) -> Optional[float]:
518+
"""Return today's hot water energy consumed in kWh."""
519+
return self.get_device_prop("DailyHotWaterEnergyConsumed")
520+
521+
@property
522+
def daily_heating_energy_produced(self) -> Optional[float]:
523+
"""Return today's heating energy produced in kWh."""
524+
return self.get_device_prop("DailyHeatingEnergyProduced")
525+
526+
@property
527+
def daily_cooling_energy_produced(self) -> Optional[float]:
528+
"""Return today's cooling energy produced in kWh."""
529+
return self.get_device_prop("DailyCoolingEnergyProduced")
530+
531+
@property
532+
def daily_hot_water_energy_produced(self) -> Optional[float]:
533+
"""Return today's hot water energy produced in kWh."""
534+
return self.get_device_prop("DailyHotWaterEnergyProduced")
535+
384536
@property
385537
def zones(self) -> Optional[List[Zone]]:
386538
"""Return zones controlled by this device.

tests/samples/atw_2zone_get.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"EffectiveFlags": 0,
33
"LocalIPAddress": null,
4+
"DemandPercentage": 75,
45
"SetTemperatureZone1": 19.5,
56
"SetTemperatureZone2": 18,
67
"RoomTemperatureZone1": 20.5,
@@ -40,4 +41,4 @@
4041
"Offline": false,
4142
"Scene": null,
4243
"SceneOwner": null
43-
}
44+
}

tests/samples/atw_2zone_listdevice.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,12 @@
159159
"Zone2Master": false,
160160
"DailyEnergyConsumedDate": "2020-01-01T00:00:00",
161161
"DailyEnergyProducedDate": "2020-01-01T00:00:00",
162+
"DailyHeatingEnergyConsumed": 5.2,
163+
"DailyCoolingEnergyConsumed": 0.0,
164+
"DailyHotWaterEnergyConsumed": 3.1,
165+
"DailyHeatingEnergyProduced": 18.5,
166+
"DailyCoolingEnergyProduced": 0.0,
167+
"DailyHotWaterEnergyProduced": 11.0,
162168
"CurrentEnergyConsumed": 1,
163169
"CurrentEnergyProduced": 5,
164170
"CurrentEnergyMode": null,
@@ -284,4 +290,4 @@
284290
"CanSetFlowTemperature": true,
285291
"CanSetTemperatureIncrementOverride": true
286292
}
287-
}
293+
}

0 commit comments

Comments
 (0)