From b4eb6eacd0d7c8cb566ae070742287a4b85cd388 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sat, 17 Feb 2024 12:49:24 +0100 Subject: [PATCH] Added additional system cylinder temperature sensors --- custom_components/mypyllant/manifest.json | 2 +- custom_components/mypyllant/sensor.py | 181 +++++++++++++++------- dev-requirements.txt | 2 +- tests/test_sensor.py | 31 +++- 4 files changed, 157 insertions(+), 59 deletions(-) diff --git a/custom_components/mypyllant/manifest.json b/custom_components/mypyllant/manifest.json index 8d59fc8..fd89f4d 100644 --- a/custom_components/mypyllant/manifest.json +++ b/custom_components/mypyllant/manifest.json @@ -10,7 +10,7 @@ "iot_class": "cloud_polling", "issue_tracker": "https://github.com/signalkraft/mypyllant-component/issues", "requirements": [ - "myPyllant==0.7.17" + "myPyllant==0.7.18" ], "version": "v0.7.3" } diff --git a/custom_components/mypyllant/sensor.py b/custom_components/mypyllant/sensor.py index 2a9777e..c47d0d4 100644 --- a/custom_components/mypyllant/sensor.py +++ b/custom_components/mypyllant/sensor.py @@ -70,6 +70,22 @@ async def create_system_sensors( ) if system.water_pressure is not None: sensors.append(lambda: SystemWaterPressureSensor(index, system_coordinator)) + if system.cylinder_temperature_sensor_top_dhw is not None: + sensors.append( + lambda: SystemTopDHWTemperatureSensor(index, system_coordinator) + ) + if system.cylinder_temperature_sensor_bottom_dhw is not None: + sensors.append( + lambda: SystemBottomDHWTemperatureSensor(index, system_coordinator) + ) + if system.cylinder_temperature_sensor_top_ch is not None: + sensors.append( + lambda: SystemTopCHTemperatureSensor(index, system_coordinator) + ) + if system.cylinder_temperature_sensor_bottom_ch is not None: + sensors.append( + lambda: SystemBottomCHTemperatureSensor(index, system_coordinator) + ) sensors.append(lambda: HomeEntity(index, system_coordinator)) for device_index, device in enumerate(system.devices): @@ -252,10 +268,99 @@ def name(self): return f"{self.name_prefix} Outdoor Temperature" +class SystemTopDHWTemperatureSensor(SystemSensor): + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_state_class = SensorStateClass.MEASUREMENT + _attr_entity_category = EntityCategory.DIAGNOSTIC + + @property + def native_value(self): + if self.system.cylinder_temperature_sensor_top_dhw is not None: + return round(self.system.cylinder_temperature_sensor_top_dhw, 1) + else: + return None + + @property + def unique_id(self) -> str: + return f"{DOMAIN}_{self.id_infix}_top_dhw_temperature" + + @property + def name(self): + return f"{self.name_prefix} Top DHW Cylinder Temperature" + + +class SystemBottomDHWTemperatureSensor(SystemSensor): + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_state_class = SensorStateClass.MEASUREMENT + _attr_entity_category = EntityCategory.DIAGNOSTIC + + @property + def native_value(self): + if self.system.cylinder_temperature_sensor_bottom_dhw is not None: + return round(self.system.cylinder_temperature_sensor_bottom_dhw, 1) + else: + return None + + @property + def unique_id(self) -> str: + return f"{DOMAIN}_{self.id_infix}_bottom_dhw_temperature" + + @property + def name(self): + return f"{self.name_prefix} Bottom DHW Cylinder Temperature" + + +class SystemTopCHTemperatureSensor(SystemSensor): + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_state_class = SensorStateClass.MEASUREMENT + _attr_entity_category = EntityCategory.DIAGNOSTIC + + @property + def native_value(self): + if self.system.cylinder_temperature_sensor_top_ch is not None: + return round(self.system.cylinder_temperature_sensor_top_ch, 1) + else: + return None + + @property + def unique_id(self) -> str: + return f"{DOMAIN}_{self.id_infix}_top_ch_temperature" + + @property + def name(self): + return f"{self.name_prefix} Top Central Heating Cylinder Temperature" + + +class SystemBottomCHTemperatureSensor(SystemSensor): + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_state_class = SensorStateClass.MEASUREMENT + _attr_entity_category = EntityCategory.DIAGNOSTIC + + @property + def native_value(self): + if self.system.cylinder_temperature_sensor_bottom_ch is not None: + return round(self.system.cylinder_temperature_sensor_bottom_ch, 1) + else: + return None + + @property + def unique_id(self) -> str: + return f"{DOMAIN}_{self.id_infix}_bottom_ch_temperature" + + @property + def name(self): + return f"{self.name_prefix} Bottom Central Heating Cylinder Temperature" + + class SystemWaterPressureSensor(SystemSensor): _attr_native_unit_of_measurement = UnitOfPressure.BAR _attr_device_class = SensorDeviceClass.PRESSURE _attr_state_class = SensorStateClass.MEASUREMENT + _attr_entity_category = EntityCategory.DIAGNOSTIC @property def native_value(self): @@ -268,16 +373,14 @@ def native_value(self): def unique_id(self) -> str: return f"{DOMAIN}_{self.id_infix}_water_pressure" - @property - def entity_category(self) -> EntityCategory | None: - return EntityCategory.DIAGNOSTIC - @property def name(self): return f"{self.name_prefix} System Water Pressure" class HomeEntity(CoordinatorEntity, SensorEntity): + _attr_entity_category = EntityCategory.DIAGNOSTIC + def __init__( self, system_index: int, @@ -290,10 +393,6 @@ def __init__( def system(self) -> System: return self.coordinator.data[self.system_index] - @property - def entity_category(self) -> EntityCategory | None: - return EntityCategory.DIAGNOSTIC - @property def extra_state_attributes(self) -> Mapping[str, Any] | None: return ( @@ -397,6 +496,8 @@ def unique_id(self) -> str: class ZoneHeatingOperatingModeSensor(ZoneCoordinatorEntity, SensorEntity): + _attr_entity_category = EntityCategory.DIAGNOSTIC + @property def name(self): return f"{self.name_prefix} Heating Operating Mode" @@ -409,12 +510,10 @@ def native_value(self): def unique_id(self) -> str: return f"{DOMAIN}_{self.id_infix}_heating_operating_mode" - @property - def entity_category(self) -> EntityCategory | None: - return EntityCategory.DIAGNOSTIC - class ZoneHeatingStateSensor(ZoneCoordinatorEntity, SensorEntity): + _attr_entity_category = EntityCategory.DIAGNOSTIC + @property def name(self): return f"{self.name_prefix} Heating State" @@ -430,12 +529,10 @@ def native_value(self): def unique_id(self) -> str: return f"{DOMAIN}_{self.id_infix}_heating_state" - @property - def entity_category(self) -> EntityCategory | None: - return EntityCategory.DIAGNOSTIC - class ZoneCurrentSpecialFunctionSensor(ZoneCoordinatorEntity, SensorEntity): + _attr_entity_category = EntityCategory.DIAGNOSTIC + @property def name(self): return f"{self.name_prefix} Current Special Function" @@ -448,10 +545,6 @@ def native_value(self): def unique_id(self) -> str: return f"{DOMAIN}_{self.id_infix}_current_special_function" - @property - def entity_category(self) -> EntityCategory | None: - return EntityCategory.DIAGNOSTIC - class CircuitSensor(CoordinatorEntity, SensorEntity): coordinator: SystemCoordinator @@ -488,6 +581,7 @@ class CircuitFlowTemperatureSensor(CircuitSensor): _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS _attr_device_class = SensorDeviceClass.TEMPERATURE _attr_state_class = SensorStateClass.MEASUREMENT + _attr_entity_category = EntityCategory.DIAGNOSTIC @property def name(self): @@ -497,16 +591,14 @@ def name(self): def native_value(self): return self.circuit.current_circuit_flow_temperature - @property - def entity_category(self) -> EntityCategory | None: - return EntityCategory.DIAGNOSTIC - @property def unique_id(self) -> str: return f"{DOMAIN}_{self.id_infix}_flow_temperature" class CircuitStateSensor(CircuitSensor): + _attr_entity_category = EntityCategory.DIAGNOSTIC + @property def name(self): return f"{self.name_prefix} State" @@ -515,10 +607,6 @@ def name(self): def native_value(self): return self.circuit.circuit_state - @property - def entity_category(self) -> EntityCategory | None: - return EntityCategory.DIAGNOSTIC - @property def extra_state_attributes(self) -> Mapping[str, Any] | None: return prepare_field_value_for_dict(self.circuit.extra_fields) @@ -532,6 +620,7 @@ class CircuitMinFlowTemperatureSetpointSensor(CircuitSensor): _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS _attr_device_class = SensorDeviceClass.TEMPERATURE _attr_state_class = SensorStateClass.MEASUREMENT + _attr_entity_category = EntityCategory.DIAGNOSTIC @property def name(self): @@ -541,10 +630,6 @@ def name(self): def native_value(self): return self.circuit.min_flow_temperature_setpoint - @property - def entity_category(self) -> EntityCategory | None: - return EntityCategory.DIAGNOSTIC - @property def unique_id(self) -> str: return f"{DOMAIN}_{self.id_infix}_min_flow_temperature_setpoint" @@ -552,6 +637,7 @@ def unique_id(self) -> str: class CircuitHeatingCurveSensor(CircuitSensor): _attr_state_class = SensorStateClass.MEASUREMENT + _attr_entity_category = EntityCategory.DIAGNOSTIC @property def name(self): @@ -564,10 +650,6 @@ def native_value(self): else: return None - @property - def entity_category(self) -> EntityCategory | None: - return EntityCategory.DIAGNOSTIC - @property def unique_id(self) -> str: return f"{DOMAIN}_{self.id_infix}_heating_curve" @@ -614,6 +696,8 @@ def unique_id(self) -> str: class DomesticHotWaterOperationModeSensor( DomesticHotWaterCoordinatorEntity, SensorEntity ): + _attr_entity_category = EntityCategory.DIAGNOSTIC + @property def name(self): return f"{self.name_prefix} Operation Mode" @@ -622,10 +706,6 @@ def name(self): def native_value(self): return self.domestic_hot_water.operation_mode_dhw.display_value - @property - def entity_category(self) -> EntityCategory: - return EntityCategory.DIAGNOSTIC - @property def unique_id(self) -> str: return f"{DOMAIN}_{self.id_infix}_operation_mode" @@ -634,6 +714,8 @@ def unique_id(self) -> str: class DomesticHotWaterCurrentSpecialFunctionSensor( DomesticHotWaterCoordinatorEntity, SensorEntity ): + _attr_entity_category = EntityCategory.DIAGNOSTIC + @property def name(self): return f"{self.name_prefix} Current Special Function" @@ -642,10 +724,6 @@ def name(self): def native_value(self): return self.domestic_hot_water.current_special_function.display_value - @property - def entity_category(self) -> EntityCategory: - return EntityCategory.DIAGNOSTIC - @property def unique_id(self) -> str: return f"{DOMAIN}_{self.id_infix}_current_special_function" @@ -908,6 +986,7 @@ class SystemDeviceWaterPressureSensor(SystemDeviceSensor): _attr_native_unit_of_measurement = UnitOfPressure.BAR _attr_device_class = SensorDeviceClass.PRESSURE _attr_state_class = SensorStateClass.MEASUREMENT + _attr_entity_category = EntityCategory.DIAGNOSTIC @property def name(self): @@ -921,15 +1000,12 @@ def native_value(self): def unique_id(self) -> str: return f"{DOMAIN}_{self.id_infix}_water_pressure" - @property - def entity_category(self) -> EntityCategory | None: - return EntityCategory.DIAGNOSTIC - class SystemDeviceOperationTimeSensor(SystemDeviceSensor): _attr_native_unit_of_measurement = UnitOfTime.HOURS _attr_device_class = SensorDeviceClass.DURATION _attr_state_class = SensorStateClass.MEASUREMENT + _attr_entity_category = EntityCategory.DIAGNOSTIC @property def native_value(self): @@ -942,10 +1018,6 @@ def native_value(self): def unique_id(self) -> str: return f"{DOMAIN}_{self.id_infix}_operation_time" - @property - def entity_category(self) -> EntityCategory | None: - return EntityCategory.DIAGNOSTIC - @property def name(self): return f"{self.name_prefix} Operation Time" @@ -953,6 +1025,7 @@ def name(self): class SystemDeviceOnOffCyclesSensor(SystemDeviceSensor): _attr_state_class = SensorStateClass.MEASUREMENT + _attr_entity_category = EntityCategory.DIAGNOSTIC _attr_icon = "mdi:counter" @property @@ -966,10 +1039,6 @@ def native_value(self): def unique_id(self) -> str: return f"{DOMAIN}_{self.id_infix}_on_off_cycles" - @property - def entity_category(self) -> EntityCategory | None: - return EntityCategory.DIAGNOSTIC - @property def name(self): return f"{self.name_prefix} On/Off Cycles" diff --git a/dev-requirements.txt b/dev-requirements.txt index 403cafe..37793b0 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -15,5 +15,5 @@ types-PyYAML~=6.0.12.12 pytest==7.4.3 pytest-cov==4.1.0 pytest-homeassistant-custom-component==0.13.77 -myPyllant==0.7.17 +myPyllant==0.7.18 dacite~=1.7.0 \ No newline at end of file diff --git a/tests/test_sensor.py b/tests/test_sensor.py index a0e4239..060cfa2 100644 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -5,7 +5,8 @@ from myPyllant.api import MyPyllantAPI from myPyllant.models import DeviceData from myPyllant.enums import CircuitState -from myPyllant.tests.utils import list_test_data +from myPyllant.tests.generate_test_data import DATA_DIR +from myPyllant.tests.utils import list_test_data, load_test_data from custom_components.mypyllant.sensor import ( CircuitFlowTemperatureSensor, @@ -28,6 +29,9 @@ SystemDeviceOnOffCyclesSensor, SystemDeviceOperationTimeSensor, create_system_sensors, + SystemTopDHWTemperatureSensor, + SystemBottomDHWTemperatureSensor, + SystemTopCHTemperatureSensor, ) from custom_components.mypyllant.const import DOMAIN from tests.conftest import MockConfigEntry, TEST_OPTIONS @@ -269,3 +273,28 @@ async def test_device_sensor( float, ) await mocked_api.aiohttp_session.close() + + +async def test_additional_system_sensors( + mypyllant_aioresponses, + mocked_api: MyPyllantAPI, + system_coordinator_mock, +): + test_data = load_test_data(DATA_DIR / "two_systems") + with mypyllant_aioresponses(test_data) as _: + system_coordinator_mock.data = ( + await system_coordinator_mock._async_update_data() + ) + assert isinstance( + SystemTopDHWTemperatureSensor(0, system_coordinator_mock).native_value, + float, + ) + assert isinstance( + SystemBottomDHWTemperatureSensor(0, system_coordinator_mock).native_value, + float, + ) + assert isinstance( + SystemTopCHTemperatureSensor(0, system_coordinator_mock).native_value, + float, + ) + await mocked_api.aiohttp_session.close()