Skip to content
Merged
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
232 changes: 220 additions & 12 deletions PyViCare/PyViCareHeatPump.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ def getInverter(self, inverter) -> Inverter:

@handleNotSupported
def getBufferMainTemperature(self):
return self.getProperty("heating.bufferCylinder.sensors.temperature.main")["properties"]['value']['value']
return self.getProperty("heating.bufferCylinder.sensors.temperature.main")["properties"]["value"]["value"]

@handleNotSupported
def getBufferTopTemperature(self):
return self.getProperty("heating.bufferCylinder.sensors.temperature.top")["properties"]['value']['value']
return self.getProperty("heating.bufferCylinder.sensors.temperature.top")["properties"]["value"]["value"]

# Power consumption for Heating:
@handleNotSupported
Expand Down Expand Up @@ -325,6 +325,24 @@ def getSeasonalPerformanceFactorHeating(self) -> float:
def getSeasonalPerformanceFactorTotal(self) -> float:
return float(self.getProperty("heating.spf.total")["properties"]["value"]["value"])

# COP (Coefficient of Performance) - instantaneous efficiency metrics
# Some devices expose COP instead of SPF
@handleNotSupported
def getCoefficientOfPerformanceHeating(self) -> float:
return float(self.getProperty("heating.cop.heating")["properties"]["value"]["value"])

@handleNotSupported
def getCoefficientOfPerformanceDHW(self) -> float:
return float(self.getProperty("heating.cop.dhw")["properties"]["value"]["value"])

@handleNotSupported
def getCoefficientOfPerformanceTotal(self) -> float:
return float(self.getProperty("heating.cop.total")["properties"]["value"]["value"])

@handleNotSupported
def getCoefficientOfPerformanceCooling(self) -> float:
return float(self.getProperty("heating.cop.cooling")["properties"]["value"]["value"])

@handleNotSupported
def getHeatingRodStarts(self) -> int:
return int(self.getProperty("heating.heatingRod.statistics")["properties"]["starts"]["value"])
Expand Down Expand Up @@ -361,6 +379,157 @@ def getHeatingRodPowerConsumptionHeatingThisYear(self) -> float:
def getHeatingRodPowerConsumptionTotalThisYear(self) -> float:
return float(self.getProperty("heating.heatingRod.power.consumption.total")["properties"]["year"]["value"][0])

# Heating rod power consumption summary for DHW:
@handleNotSupported
def getHeatingRodPowerConsumptionSummaryDHWUnit(self) -> str:
return str(self.getProperty("heating.heatingRod.power.consumption.summary.dhw")["properties"]["currentDay"]["unit"])

@handleNotSupported
def getHeatingRodPowerConsumptionSummaryDHWCurrentDay(self) -> float:
return float(self.getProperty("heating.heatingRod.power.consumption.summary.dhw")["properties"]["currentDay"]["value"])

@handleNotSupported
def getHeatingRodPowerConsumptionSummaryDHWCurrentMonth(self) -> float:
return float(self.getProperty("heating.heatingRod.power.consumption.summary.dhw")["properties"]["currentMonth"]["value"])

@handleNotSupported
def getHeatingRodPowerConsumptionSummaryDHWCurrentYear(self) -> float:
return float(self.getProperty("heating.heatingRod.power.consumption.summary.dhw")["properties"]["currentYear"]["value"])

@handleNotSupported
def getHeatingRodPowerConsumptionSummaryDHWLastMonth(self) -> float:
return float(self.getProperty("heating.heatingRod.power.consumption.summary.dhw")["properties"]["lastMonth"]["value"])

@handleNotSupported
def getHeatingRodPowerConsumptionSummaryDHWLastSevenDays(self) -> float:
return float(self.getProperty("heating.heatingRod.power.consumption.summary.dhw")["properties"]["lastSevenDays"]["value"])

@handleNotSupported
def getHeatingRodPowerConsumptionSummaryDHWLastYear(self) -> float:
return float(self.getProperty("heating.heatingRod.power.consumption.summary.dhw")["properties"]["lastYear"]["value"])

# Heating rod power consumption summary for Heating:
@handleNotSupported
def getHeatingRodPowerConsumptionSummaryHeatingUnit(self) -> str:
return str(self.getProperty("heating.heatingRod.power.consumption.summary.heating")["properties"]["currentDay"]["unit"])

@handleNotSupported
def getHeatingRodPowerConsumptionSummaryHeatingCurrentDay(self) -> float:
return float(self.getProperty("heating.heatingRod.power.consumption.summary.heating")["properties"]["currentDay"]["value"])

@handleNotSupported
def getHeatingRodPowerConsumptionSummaryHeatingCurrentMonth(self) -> float:
return float(self.getProperty("heating.heatingRod.power.consumption.summary.heating")["properties"]["currentMonth"]["value"])

@handleNotSupported
def getHeatingRodPowerConsumptionSummaryHeatingCurrentYear(self) -> float:
return float(self.getProperty("heating.heatingRod.power.consumption.summary.heating")["properties"]["currentYear"]["value"])

@handleNotSupported
def getHeatingRodPowerConsumptionSummaryHeatingLastMonth(self) -> float:
return float(self.getProperty("heating.heatingRod.power.consumption.summary.heating")["properties"]["lastMonth"]["value"])

@handleNotSupported
def getHeatingRodPowerConsumptionSummaryHeatingLastSevenDays(self) -> float:
return float(self.getProperty("heating.heatingRod.power.consumption.summary.heating")["properties"]["lastSevenDays"]["value"])

@handleNotSupported
def getHeatingRodPowerConsumptionSummaryHeatingLastYear(self) -> float:
return float(self.getProperty("heating.heatingRod.power.consumption.summary.heating")["properties"]["lastYear"]["value"])

# Heating rod runtime by level
@handleNotSupported
def getHeatingRodRuntimeLevelOne(self) -> int:
return int(self.getProperty("heating.heatingRod.runtime")["properties"]["levelOne"]["value"])

@handleNotSupported
def getHeatingRodRuntimeLevelTwo(self) -> int:
return int(self.getProperty("heating.heatingRod.runtime")["properties"]["levelTwo"]["value"])

@handleNotSupported
def getHeatingRodRuntimeLevelOneUnit(self) -> str:
return str(self.getProperty("heating.heatingRod.runtime")["properties"]["levelOne"]["unit"])

# Additional pressure sensors (refrigerant circuit)
@handleNotSupported
def getHotGasPressure(self) -> float:
return float(self.getProperty("heating.sensors.pressure.hotGas")["properties"]["value"]["value"])

@handleNotSupported
def getHotGasPressureUnit(self) -> str:
return str(self.getProperty("heating.sensors.pressure.hotGas")["properties"]["value"]["unit"])

@handleNotSupported
def getSuctionGasPressure(self) -> float:
return float(self.getProperty("heating.sensors.pressure.suctionGas")["properties"]["value"]["value"])

@handleNotSupported
def getSuctionGasPressureUnit(self) -> str:
return str(self.getProperty("heating.sensors.pressure.suctionGas")["properties"]["value"]["unit"])

# Additional temperature sensors (refrigerant circuit)
@handleNotSupported
def getHotGasTemperature(self) -> float:
return float(self.getProperty("heating.sensors.temperature.hotGas")["properties"]["value"]["value"])

@handleNotSupported
def getHotGasTemperatureUnit(self) -> str:
return str(self.getProperty("heating.sensors.temperature.hotGas")["properties"]["value"]["unit"])

@handleNotSupported
def getLiquidGasTemperature(self) -> float:
return float(self.getProperty("heating.sensors.temperature.liquidGas")["properties"]["value"]["value"])

@handleNotSupported
def getLiquidGasTemperatureUnit(self) -> str:
return str(self.getProperty("heating.sensors.temperature.liquidGas")["properties"]["value"]["unit"])

@handleNotSupported
def getSuctionGasTemperature(self) -> float:
return float(self.getProperty("heating.sensors.temperature.suctionGas")["properties"]["value"]["value"])

@handleNotSupported
def getSuctionGasTemperatureUnit(self) -> str:
return str(self.getProperty("heating.sensors.temperature.suctionGas")["properties"]["value"]["unit"])

# Main ECU runtime
@handleNotSupported
def getMainECURuntime(self) -> int:
return int(self.getProperty("heating.device.mainECU")["properties"]["runtime"]["value"])

@handleNotSupported
def getMainECURuntimeUnit(self) -> str:
return str(self.getProperty("heating.device.mainECU")["properties"]["runtime"]["unit"])

# Configuration values
@handleNotSupported
def getConfigurationBufferTemperatureMax(self) -> float:
return float(self.getProperty("heating.configuration.buffer.temperature.max")["properties"]["value"]["value"])

@handleNotSupported
def getConfigurationBufferTemperatureMaxUnit(self) -> str:
return str(self.getProperty("heating.configuration.buffer.temperature.max")["properties"]["value"]["unit"])

@handleNotSupported
def getConfigurationOutsideTemperatureDampingFactor(self) -> int:
return int(self.getProperty("heating.configuration.temperature.outside.DampingFactor")["properties"]["value"]["value"])

@handleNotSupported
def getConfigurationOutsideTemperatureDampingFactorUnit(self) -> str:
return str(self.getProperty("heating.configuration.temperature.outside.DampingFactor")["properties"]["value"]["unit"])

@handleNotSupported
def getConfigurationHeatingRodDHWApproved(self) -> bool:
return bool(self.getProperty("heating.configuration.heatingRod.dhw")["properties"]["useApproved"]["value"])

@handleNotSupported
def getConfigurationHeatingRodHeatingApproved(self) -> bool:
return bool(self.getProperty("heating.configuration.heatingRod.heating")["properties"]["useApproved"]["value"])

@handleNotSupported
def getConfigurationDHWHeaterApproved(self) -> bool:
return bool(self.getProperty("heating.configuration.dhwHeater")["properties"]["useApproved"]["value"])

# Cooling circuits
@property
def coolingCircuits(self) -> List[CoolingCircuit]:
Expand Down Expand Up @@ -395,6 +564,7 @@ def getReverseActive(self) -> bool:
return bool(self.getProperty(f"heating.coolingCircuits.{self.circuit}.reverse")["properties"]["active"]["value"])



class Compressor(HeatingDeviceWithComponent):

@property
Expand All @@ -409,25 +579,45 @@ def getStarts(self):
def getHours(self):
return self.getProperty(f"heating.compressors.{self.compressor}.statistics")["properties"]["hours"]["value"]

@handleNotSupported
def getHoursLoadClass1(self):
return self.getProperty(f"heating.compressors.{self.compressor}.statistics")["properties"]["hoursLoadClassOne"]["value"]
"""Get hours in load class 1. Tries 'statistics' path first, then 'statistics.load'."""
with suppress(KeyError):
return self.getProperty(f"heating.compressors.{self.compressor}.statistics")["properties"]["hoursLoadClassOne"]["value"]
with suppress(KeyError):
return self.getProperty(f"heating.compressors.{self.compressor}.statistics.load")["properties"]["hoursLoadClassOne"]["value"]
raise PyViCareNotSupportedFeatureError("getHoursLoadClass1")

@handleNotSupported
def getHoursLoadClass2(self):
return self.getProperty(f"heating.compressors.{self.compressor}.statistics")["properties"]["hoursLoadClassTwo"]["value"]
"""Get hours in load class 2. Tries 'statistics' path first, then 'statistics.load'."""
with suppress(KeyError):
return self.getProperty(f"heating.compressors.{self.compressor}.statistics")["properties"]["hoursLoadClassTwo"]["value"]
with suppress(KeyError):
return self.getProperty(f"heating.compressors.{self.compressor}.statistics.load")["properties"]["hoursLoadClassTwo"]["value"]
raise PyViCareNotSupportedFeatureError("getHoursLoadClass2")

@handleNotSupported
def getHoursLoadClass3(self):
return self.getProperty(f"heating.compressors.{self.compressor}.statistics")["properties"]["hoursLoadClassThree"]["value"]
"""Get hours in load class 3. Tries 'statistics' path first, then 'statistics.load'."""
with suppress(KeyError):
return self.getProperty(f"heating.compressors.{self.compressor}.statistics")["properties"]["hoursLoadClassThree"]["value"]
with suppress(KeyError):
return self.getProperty(f"heating.compressors.{self.compressor}.statistics.load")["properties"]["hoursLoadClassThree"]["value"]
raise PyViCareNotSupportedFeatureError("getHoursLoadClass3")

@handleNotSupported
def getHoursLoadClass4(self):
return self.getProperty(f"heating.compressors.{self.compressor}.statistics")["properties"]["hoursLoadClassFour"]["value"]
"""Get hours in load class 4. Tries 'statistics' path first, then 'statistics.load'."""
with suppress(KeyError):
return self.getProperty(f"heating.compressors.{self.compressor}.statistics")["properties"]["hoursLoadClassFour"]["value"]
with suppress(KeyError):
return self.getProperty(f"heating.compressors.{self.compressor}.statistics.load")["properties"]["hoursLoadClassFour"]["value"]
raise PyViCareNotSupportedFeatureError("getHoursLoadClass4")

@handleNotSupported
def getHoursLoadClass5(self):
return self.getProperty(f"heating.compressors.{self.compressor}.statistics")["properties"]["hoursLoadClassFive"]["value"]
"""Get hours in load class 5. Tries 'statistics' path first, then 'statistics.load'."""
with suppress(KeyError):
return self.getProperty(f"heating.compressors.{self.compressor}.statistics")["properties"]["hoursLoadClassFive"]["value"]
with suppress(KeyError):
return self.getProperty(f"heating.compressors.{self.compressor}.statistics.load")["properties"]["hoursLoadClassFive"]["value"]
raise PyViCareNotSupportedFeatureError("getHoursLoadClass5")

@handleNotSupported
def getActive(self):
Expand All @@ -437,6 +627,24 @@ def getActive(self):
def getPhase(self):
return self.getProperty(f"heating.compressors.{self.compressor}")["properties"]["phase"]["value"]

@handleNotSupported
def getPower(self) -> float:
# Returns the nominal/maximum power of the compressor in kW
return float(self.getProperty(f"heating.compressors.{self.compressor}.power")["properties"]["value"]["value"])

@handleNotSupported
def getPowerUnit(self) -> str:
return str(self.getProperty(f"heating.compressors.{self.compressor}.power")["properties"]["value"]["unit"])

@handleNotSupported
def getModulation(self) -> int:
# Returns the current compressor modulation/power level as percentage (0-100)
return int(self.getProperty(f"heating.compressors.{self.compressor}.sensors.power")["properties"]["value"]["value"])

@handleNotSupported
def getModulationUnit(self) -> str:
return str(self.getProperty(f"heating.compressors.{self.compressor}.sensors.power")["properties"]["value"]["unit"])

@handleNotSupported
def getSpeed(self) -> int:
return int(self.getProperty(f"heating.compressors.{self.compressor}.speed.current")["properties"]["value"]["value"])
Expand Down
18 changes: 18 additions & 0 deletions PyViCare/PyViCareHeatingDevice.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,15 @@ def getReturnTemperaturePrimaryCircuit(self):
return self.getProperty("heating.primaryCircuit.sensors.temperature.return")["properties"]["value"][
"value"]

@handleNotSupported
def getPrimaryCircuitPumpRotation(self) -> float:
"""Get primary circuit pump rotation/speed as percentage."""
return float(self.getProperty("heating.primaryCircuit.sensors.rotation")["properties"]["value"]["value"])

@handleNotSupported
def getPrimaryCircuitPumpRotationUnit(self):
return self.getProperty("heating.primaryCircuit.sensors.rotation")["properties"]["value"]["unit"]

@handleNotSupported
def getSupplyTemperatureSecondaryCircuit(self):
return self.getProperty("heating.secondaryCircuit.sensors.temperature.supply")["properties"]["value"][
Expand Down Expand Up @@ -548,6 +557,15 @@ def getSupplyTemperature(self):
self.getProperty(f"heating.circuits.{self.circuit}.sensors.temperature.supply")["properties"][
"value"]["value"]

@handleNotSupported
def getTargetTemperature(self) -> float:
"""Get the circuit target temperature."""
return float(self.getProperty(f"heating.circuits.{self.circuit}.temperature")["properties"]["value"]["value"])

@handleNotSupported
def getTargetTemperatureUnit(self):
return self.getProperty(f"heating.circuits.{self.circuit}.temperature")["properties"]["value"]["unit"]

@handleNotSupported
def getRoomTemperature(self):
return self.getProperty(f"heating.circuits.{self.circuit}.sensors.temperature.room")["properties"][
Expand Down
15 changes: 11 additions & 4 deletions tests/test_TestForMissingProperties.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ def test_deprecatedProperties(self):
'ventilation.operating.programs.levelTwo',
'ventilation.operating.programs.forcedLevelFour',
'ventilation.operating.programs.silent',
# Alternative naming conventions used as fallback for device compatibility
'heating.buffer.sensors.temperature.main',
'heating.buffer.sensors.temperature.top',
'heating.dhw.sensors.temperature.hotWaterStorage',
'heating.dhw.sensors.temperature.hotWaterStorage.top',
'heating.dhw.sensors.temperature.hotWaterStorage.bottom',
'heating.dhw.sensors.temperature.hotWaterStorage.middle',
'heating.dhw.sensors.temperature.hotWaterStorage.midBottom',
'heating.cop.green', # deprecated, replaced by heating.cop.photovoltaic
]

all_features = self.read_all_deprecated_features()
Expand Down Expand Up @@ -201,12 +210,11 @@ def test_missingProperties(self):
'heating.compressors.0.sensors.power',
'heating.compressors.0.statistics.load',
'heating.configuration.buffer.temperature.max',
'heating.configuration.dhwHeater',
'heating.configuration.flow.temperature.max',
'heating.configuration.flow.temperature.min',
'heating.cop.cooling',
'heating.cop.dhw',
'heating.cop.green',
'heating.cop.green', # deprecated, replaced by heating.cop.photovoltaic
'heating.cop.heating',
'heating.cop.total',
'heating.heatingRod.heatTarget',
Expand All @@ -217,8 +225,6 @@ def test_missingProperties(self):
'heating.sensors.temperature.hotGas',
'heating.sensors.temperature.liquidGas',
'heating.sensors.temperature.suctionGas',
'heating.heatingRod.power.consumption.summary.dhw',
'heating.heatingRod.power.consumption.summary.heating',
'heating.heatingRod.status',
'heating.scop.dhw', # deprecated
'heating.scop.heating', # deprecated
Expand Down Expand Up @@ -362,6 +368,7 @@ def test_unverifiedProperties(self):
for match in re.findall(r'getProperty\(\s*?f?"(.*)"\s*?\)', all_python_files[python]):
feature_name = re.sub(r'{(self\.)?(circuit|burner|compressor|condensor|evaporator|inverter)}', '0', match)
feature_name = re.sub(r'{burner}', '0', feature_name)
feature_name = re.sub(r'{circuit}', '0', feature_name) # for local variable in loops
feature_name = re.sub(r'\.{(quickmode|mode|program|active_program)}', '', feature_name)
used_features.append(feature_name)

Expand Down
Loading
Loading