Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
190 changes: 175 additions & 15 deletions src/pymelcloud/atw_device.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Air-To-Water (DeviceType=1) device definition."""

import warnings
from typing import Any, Callable, Dict, List, Optional

from pymelcloud.device import EFFECTIVE_FLAGS, Device
Expand All @@ -9,11 +11,15 @@
PROPERTY_ZONE_2_TARGET_TEMPERATURE = "zone_2_target_temperature"
PROPERTY_ZONE_1_TARGET_HEAT_FLOW_TEMPERATURE = "zone_1_target_heat_flow_temperature"
PROPERTY_ZONE_2_TARGET_HEAT_FLOW_TEMPERATURE = "zone_2_target_heat_flow_temperature"
PROPERTY_ZONE_1_TARGET_COOL_FLOW_TEMPERATURE = "zone_1_target_heat_cool_temperature"
PROPERTY_ZONE_2_TARGET_COOL_FLOW_TEMPERATURE = "zone_2_target_heat_cool_temperature"
PROPERTY_ZONE_1_TARGET_COOL_FLOW_TEMPERATURE = "zone_1_target_cool_flow_temperature"
PROPERTY_ZONE_2_TARGET_COOL_FLOW_TEMPERATURE = "zone_2_target_cool_flow_temperature"
PROPERTY_ZONE_1_OPERATION_MODE = "zone_1_operation_mode"
PROPERTY_ZONE_2_OPERATION_MODE = "zone_2_operation_mode"

# Deprecated aliases for backwards compatibility
PROPERTY_ZONE_1_TARGET_HEAT_COOL_TEMPERATURE = "zone_1_target_heat_cool_temperature"
PROPERTY_ZONE_2_TARGET_HEAT_COOL_TEMPERATURE = "zone_2_target_heat_cool_temperature"

OPERATION_MODE_AUTO = "auto"
OPERATION_MODE_FORCE_HOT_WATER = "force_hot_water"

Expand Down Expand Up @@ -155,22 +161,60 @@ async def set_target_temperature(self, target_temperature):
await self._device.set({prop: target_temperature})

@property
def flow_temperature(self) -> float:
def flow_temperature(self) -> Optional[float]:
"""Return current flow temperature.

This value is not available in the standard state poll response. The poll
update frequency can be a little bit lower that expected.
.. deprecated::
Use `device.flow_temperature` or `zone.zone_flow_temperature` instead.
"""
return self._device_conf()["Device"]["FlowTemperature"]
warnings.warn(
"Zone.flow_temperature is deprecated, use device.flow_temperature "
"or zone.zone_flow_temperature instead",
DeprecationWarning,
stacklevel=2,
)
result: Optional[float] = (
self._device_conf().get("Device", {}).get("FlowTemperature")
)
return result

@property
def return_temperature(self) -> float:
"""Return current return flow temperature.
def return_temperature(self) -> Optional[float]:
"""Return current return temperature.

This value is not available in the standard state poll response. The poll
update frequency can be a little bit lower that expected.
.. deprecated::
Use `device.return_temperature` or `zone.zone_return_temperature` instead.
"""
return self._device_conf()["Device"]["ReturnTemperature"]
warnings.warn(
"Zone.return_temperature is deprecated, use device.return_temperature "
"or zone.zone_return_temperature instead",
DeprecationWarning,
stacklevel=2,
)
result: Optional[float] = (
self._device_conf().get("Device", {}).get("ReturnTemperature")
)
return result

@property
def zone_flow_temperature(self) -> Optional[float]:
"""Return current zone-specific flow temperature."""
result: Optional[float] = (
self._device_conf()
.get("Device", {})
.get(f"FlowTemperatureZone{self.zone_index}")
)
return result

@property
def zone_return_temperature(self) -> Optional[float]:
"""Return current zone-specific return temperature."""
result: Optional[float] = (
self._device_conf()
.get("Device", {})
.get(f"ReturnTemperatureZone{self.zone_index}")
)
return result

@property
def target_flow_temperature(self) -> Optional[float]:
Expand Down Expand Up @@ -309,13 +353,19 @@ def apply_write(self, state: Dict[str, Any], key: str, value: Any):
elif key == PROPERTY_ZONE_1_TARGET_HEAT_FLOW_TEMPERATURE:
state["SetHeatFlowTemperatureZone1"] = self.round_temperature(value)
flags |= 0x1000000000000
elif key == PROPERTY_ZONE_1_TARGET_COOL_FLOW_TEMPERATURE:
elif key in (
PROPERTY_ZONE_1_TARGET_COOL_FLOW_TEMPERATURE,
PROPERTY_ZONE_1_TARGET_HEAT_COOL_TEMPERATURE,
):
state["SetCoolFlowTemperatureZone1"] = self.round_temperature(value)
flags |= 0x1000000000000
elif key == PROPERTY_ZONE_2_TARGET_HEAT_FLOW_TEMPERATURE:
state["SetHeatFlowTemperatureZone2"] = self.round_temperature(value)
flags |= 0x1000000000000
elif key == PROPERTY_ZONE_2_TARGET_COOL_FLOW_TEMPERATURE:
elif key in (
PROPERTY_ZONE_2_TARGET_COOL_FLOW_TEMPERATURE,
PROPERTY_ZONE_2_TARGET_HEAT_COOL_TEMPERATURE,
):
state["SetCoolFlowTemperatureZone2"] = self.round_temperature(value)
flags |= 0x1000000000000
elif key == PROPERTY_ZONE_1_OPERATION_MODE:
Expand Down Expand Up @@ -366,21 +416,131 @@ def outside_temperature(self) -> Optional[float]:
"""
return self.get_state_prop("OutdoorTemperature")

@property
def flow_temperature(self) -> Optional[float]:
"""Return current flow temperature of the entire system."""
return self.get_device_prop("FlowTemperature")

@property
def return_temperature(self) -> Optional[float]:
"""Return current return temperature of the entire system."""
return self.get_device_prop("ReturnTemperature")

@property
def flow_temperature_boiler(self) -> Optional[float]:
"""Return flow temperature of the boiler."""
return self.get_device_prop("FlowTemperatureBoiler")

@property
def return_temperature_boiler(self) -> Optional[float]:
"""Return flow temperature of the boiler."""
return self.get_device_prop("FlowTemperatureBoiler")
"""Return the boiler return temperature."""
return self.get_device_prop("ReturnTemperatureBoiler")

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

@property
def condensing_temperature(self) -> Optional[float]:
"""Return condensing temperature."""
return self.get_device_prop("CondensingTemperature")

@property
def heat_pump_frequency(self) -> Optional[int]:
"""Return current heat pump compressor frequency in Hz."""
return self.get_device_prop("HeatPumpFrequency")

@property
def demand_percentage(self) -> Optional[int]:
"""Return demand percentage (0-100)."""
return self.get_state_prop("DemandPercentage")

@property
def boiler_status(self) -> Optional[bool]:
"""Return boiler status."""
return self.get_device_prop("BoilerStatus")

@property
def booster_heater1_status(self) -> Optional[bool]:
"""Return booster heater 1 status."""
return self.get_device_prop("BoosterHeater1Status")

@property
def booster_heater2_status(self) -> Optional[bool]:
"""Return booster heater 2 status."""
return self.get_device_prop("BoosterHeater2Status")

@property
def booster_heater2plus_status(self) -> Optional[bool]:
"""Return booster heater 2+ status."""
return self.get_device_prop("BoosterHeater2PlusStatus")

@property
def immersion_heater_status(self) -> Optional[bool]:
"""Return immersion heater status."""
return self.get_device_prop("ImmersionHeaterStatus")

@property
def water_pump1_status(self) -> Optional[bool]:
"""Return water pump 1 status."""
return self.get_device_prop("WaterPump1Status")

@property
def water_pump2_status(self) -> Optional[bool]:
"""Return water pump 2 status."""
return self.get_device_prop("WaterPump2Status")

@property
def water_pump3_status(self) -> Optional[bool]:
"""Return water pump 3 status."""
return self.get_device_prop("WaterPump3Status")

@property
def water_pump4_status(self) -> Optional[bool]:
"""Return water pump 4 status."""
return self.get_device_prop("WaterPump4Status")

@property
def valve_3way_status(self) -> Optional[bool]:
"""Return 3-way valve status."""
return self.get_device_prop("ValveStatus3Way")

@property
def valve_2way_status(self) -> Optional[bool]:
"""Return 2-way valve status."""
return self.get_device_prop("ValveStatus2Way")

@property
def daily_heating_energy_consumed(self) -> Optional[float]:
"""Return today's heating energy consumed in kWh."""
return self.get_device_prop("DailyHeatingEnergyConsumed")

@property
def daily_cooling_energy_consumed(self) -> Optional[float]:
"""Return today's cooling energy consumed in kWh."""
return self.get_device_prop("DailyCoolingEnergyConsumed")

@property
def daily_hot_water_energy_consumed(self) -> Optional[float]:
"""Return today's hot water energy consumed in kWh."""
return self.get_device_prop("DailyHotWaterEnergyConsumed")

@property
def daily_heating_energy_produced(self) -> Optional[float]:
"""Return today's heating energy produced in kWh."""
return self.get_device_prop("DailyHeatingEnergyProduced")

@property
def daily_cooling_energy_produced(self) -> Optional[float]:
"""Return today's cooling energy produced in kWh."""
return self.get_device_prop("DailyCoolingEnergyProduced")

@property
def daily_hot_water_energy_produced(self) -> Optional[float]:
"""Return today's hot water energy produced in kWh."""
return self.get_device_prop("DailyHotWaterEnergyProduced")

@property
def zones(self) -> Optional[List[Zone]]:
"""Return zones controlled by this device.
Expand Down
3 changes: 2 additions & 1 deletion tests/samples/atw_2zone_get.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"EffectiveFlags": 0,
"LocalIPAddress": null,
"DemandPercentage": 75,
"SetTemperatureZone1": 19.5,
"SetTemperatureZone2": 18,
"RoomTemperatureZone1": 20.5,
Expand Down Expand Up @@ -40,4 +41,4 @@
"Offline": false,
"Scene": null,
"SceneOwner": null
}
}
8 changes: 7 additions & 1 deletion tests/samples/atw_2zone_listdevice.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,12 @@
"Zone2Master": false,
"DailyEnergyConsumedDate": "2020-01-01T00:00:00",
"DailyEnergyProducedDate": "2020-01-01T00:00:00",
"DailyHeatingEnergyConsumed": 5.2,
"DailyCoolingEnergyConsumed": 0.0,
"DailyHotWaterEnergyConsumed": 3.1,
"DailyHeatingEnergyProduced": 18.5,
"DailyCoolingEnergyProduced": 0.0,
"DailyHotWaterEnergyProduced": 11.0,
"CurrentEnergyConsumed": 1,
"CurrentEnergyProduced": 5,
"CurrentEnergyMode": null,
Expand Down Expand Up @@ -284,4 +290,4 @@
"CanSetFlowTemperature": true,
"CanSetTemperatureIncrementOverride": true
}
}
}
Loading
Loading