This commit is contained in:
Jean-Marc Collin
2023-02-12 23:52:04 +01:00
parent 7a917c6ff7
commit 81b4f7e5f6
4 changed files with 156 additions and 42 deletions

View File

@@ -85,6 +85,12 @@ class PropAlgorithm:
def _calculate_internal(self): def _calculate_internal(self):
"""Finish the calculation to get the on_percent in seconds""" """Finish the calculation to get the on_percent in seconds"""
# calculated on_time duration in seconds
if self._calculated_on_percent > 1:
self._calculated_on_percent = 1
if self._calculated_on_percent < 0:
self._calculated_on_percent = 0
if self._security: if self._security:
_LOGGER.debug( _LOGGER.debug(
"Security is On using the default_on_percent %f", "Security is On using the default_on_percent %f",
@@ -98,11 +104,6 @@ class PropAlgorithm:
) )
self._on_percent = self._calculated_on_percent self._on_percent = self._calculated_on_percent
# calculated on_time duration in seconds
if self._on_percent > 1:
self._on_percent = 1
if self._on_percent < 0:
self._on_percent = 0
self._on_time_sec = self._on_percent * self._cycle_min * 60 self._on_time_sec = self._on_percent * self._cycle_min * 60
# Do not heat for less than xx sec # Do not heat for less than xx sec

View File

@@ -1,16 +1,58 @@
""" Some common resources """ """ Some common resources """
from unittest.mock import patch
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.components.climate import ClimateEntity
from homeassistant.const import UnitOfTemperature from homeassistant.const import UnitOfTemperature
from homeassistant.config_entries import ConfigEntryState
from pytest_homeassistant_custom_component.common import MockConfigEntry
from homeassistant.helpers.entity_component import EntityComponent
from ..climate import VersatileThermostat
from ..const import DOMAIN
from homeassistant.components.climate import ( from homeassistant.components.climate import (
ClimateEntity,
DOMAIN as CLIMATE_DOMAIN, DOMAIN as CLIMATE_DOMAIN,
ATTR_PRESET_MODE, ATTR_PRESET_MODE,
HVACMode, HVACMode,
HVACAction, HVACAction,
) )
from .const import (
MOCK_TH_OVER_SWITCH_USER_CONFIG,
MOCK_TH_OVER_CLIMATE_USER_CONFIG,
MOCK_TH_OVER_SWITCH_TYPE_CONFIG,
MOCK_TH_OVER_CLIMATE_TYPE_CONFIG,
MOCK_TH_OVER_SWITCH_TPI_CONFIG,
MOCK_PRESETS_CONFIG,
MOCK_WINDOW_CONFIG,
MOCK_MOTION_CONFIG,
MOCK_POWER_CONFIG,
MOCK_PRESENCE_CONFIG,
MOCK_ADVANCED_CONFIG,
# MOCK_DEFAULT_FEATURE_CONFIG,
)
FULL_SWITCH_CONFIG = (
MOCK_TH_OVER_SWITCH_USER_CONFIG
| MOCK_TH_OVER_SWITCH_TYPE_CONFIG
| MOCK_TH_OVER_SWITCH_TPI_CONFIG
| MOCK_PRESETS_CONFIG
| MOCK_WINDOW_CONFIG
| MOCK_MOTION_CONFIG
| MOCK_POWER_CONFIG
| MOCK_PRESENCE_CONFIG
| MOCK_ADVANCED_CONFIG
)
PARTIAL_CLIMATE_CONFIG = (
MOCK_TH_OVER_CLIMATE_USER_CONFIG
| MOCK_TH_OVER_CLIMATE_TYPE_CONFIG
| MOCK_PRESETS_CONFIG
| MOCK_ADVANCED_CONFIG
)
class MockClimate(ClimateEntity): class MockClimate(ClimateEntity):
"""A Mock Climate class used for Underlying climate mode""" """A Mock Climate class used for Underlying climate mode"""
@@ -28,3 +70,26 @@ class MockClimate(ClimateEntity):
self._attr_hvac_mode = HVACMode.OFF self._attr_hvac_mode = HVACMode.OFF
self._attr_hvac_modes = [HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT] self._attr_hvac_modes = [HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT]
self._attr_temperature_unit = UnitOfTemperature.CELSIUS self._attr_temperature_unit = UnitOfTemperature.CELSIUS
async def create_thermostat(
hass: HomeAssistant, entry: MockConfigEntry, entity_id: str
) -> VersatileThermostat:
"""Creates and return a TPI Thermostat"""
with patch(
"custom_components.versatile_thermostat.climate.VersatileThermostat.send_event"
):
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
assert entry.state is ConfigEntryState.LOADED
def find_my_entity(entity_id) -> ClimateEntity:
"""Find my new entity"""
component: EntityComponent[ClimateEntity] = hass.data[CLIMATE_DOMAIN]
for entity in component.entities:
if entity.entity_id == entity_id:
return entity
entity = find_my_entity(entity_id)
return entity

View File

@@ -15,41 +15,7 @@ from ..climate import VersatileThermostat
from ..const import DOMAIN, EventType from ..const import DOMAIN, EventType
from .const import ( from .commons import MockClimate, FULL_SWITCH_CONFIG, PARTIAL_CLIMATE_CONFIG
MOCK_TH_OVER_SWITCH_USER_CONFIG,
MOCK_TH_OVER_CLIMATE_USER_CONFIG,
MOCK_TH_OVER_SWITCH_TYPE_CONFIG,
MOCK_TH_OVER_CLIMATE_TYPE_CONFIG,
MOCK_TH_OVER_SWITCH_TPI_CONFIG,
MOCK_PRESETS_CONFIG,
MOCK_WINDOW_CONFIG,
MOCK_MOTION_CONFIG,
MOCK_POWER_CONFIG,
MOCK_PRESENCE_CONFIG,
MOCK_ADVANCED_CONFIG,
# MOCK_DEFAULT_FEATURE_CONFIG,
)
from .commons import MockClimate
FULL_SWITCH_CONFIG = (
MOCK_TH_OVER_SWITCH_USER_CONFIG
| MOCK_TH_OVER_SWITCH_TYPE_CONFIG
| MOCK_TH_OVER_SWITCH_TPI_CONFIG
| MOCK_PRESETS_CONFIG
| MOCK_WINDOW_CONFIG
| MOCK_MOTION_CONFIG
| MOCK_POWER_CONFIG
| MOCK_PRESENCE_CONFIG
| MOCK_ADVANCED_CONFIG
)
PARTIAL_CLIMATE_CONFIG = (
MOCK_TH_OVER_CLIMATE_USER_CONFIG
| MOCK_TH_OVER_CLIMATE_TYPE_CONFIG
| MOCK_PRESETS_CONFIG
| MOCK_ADVANCED_CONFIG
)
async def test_over_switch_full_start(hass: HomeAssistant, skip_hass_states_is_state): async def test_over_switch_full_start(hass: HomeAssistant, skip_hass_states_is_state):
@@ -76,7 +42,7 @@ async def test_over_switch_full_start(hass: HomeAssistant, skip_hass_states_is_s
if entity.entity_id == entity_id: if entity.entity_id == entity_id:
return entity return entity
entity = find_my_entity("climate.theoverswitchmockname") entity: VersatileThermostat = find_my_entity("climate.theoverswitchmockname")
assert entity assert entity
@@ -90,6 +56,7 @@ async def test_over_switch_full_start(hass: HomeAssistant, skip_hass_states_is_s
assert entity._window_state is None assert entity._window_state is None
assert entity._motion_state is None assert entity._motion_state is None
assert entity._presence_state is None assert entity._presence_state is None
assert entity._prop_algorithm is not None
# should have been called with EventType.PRESET_EVENT and EventType.HVAC_MODE_EVENT # should have been called with EventType.PRESET_EVENT and EventType.HVAC_MODE_EVENT
assert mock_send_event.call_count == 2 assert mock_send_event.call_count == 2

View File

@@ -0,0 +1,81 @@
""" Test the TPI algorithm """
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
async def test_tpi_calculation(hass: HomeAssistant, skip_hass_states_is_state):
"""Test the TPI calculation"""
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverSwitchMockName",
unique_id="uniqueId",
data={
"name": "TheOverSwitchMockName",
"thermostat_type": "thermostat_over_switch",
"temperature_sensor_entity_id": "sensor.mock_temp_sensor",
"external_temperature_sensor_entity_id": "sensor.mock_ext_temp_sensor",
"cycle_min": 5,
"temp_min": 15,
"temp_max": 30,
"use_window_feature": False,
"use_motion_feature": False,
"use_power_feature": False,
"use_presence_feature": False,
"heater_entity_id": "switch.mock_switch",
"proportional_function": "tpi",
"tpi_coef_int": 0.3,
"tpi_coef_ext": 0.01,
"minimal_activation_delay": 30,
"security_delay_min": 5,
"security_default_on_percent": 0.3,
},
)
entity: VersatileThermostat = await create_thermostat(
hass, entry, "climate.theoverswitchmockname"
)
assert entity
tpi_algo = entity._prop_algorithm
assert tpi_algo
tpi_algo.calculate(15, 10, 7)
assert tpi_algo.on_percent == 1
assert tpi_algo.calculated_on_percent == 1
assert tpi_algo.on_time_sec == 300
assert tpi_algo.off_time_sec == 0
tpi_algo.calculate(15, 14, 5)
assert tpi_algo.on_percent == 0.4
assert tpi_algo.calculated_on_percent == 0.4
assert tpi_algo.on_time_sec == 120
assert tpi_algo.off_time_sec == 180
tpi_algo.set_security(0.1)
tpi_algo.calculate(15, 14, 5)
assert tpi_algo.on_percent == 0.1
assert tpi_algo.calculated_on_percent == 0.4
assert tpi_algo.on_time_sec == 30 # >= minimal_activation_delay (=30)
assert tpi_algo.off_time_sec == 270
tpi_algo.unset_security()
tpi_algo.calculate(15, 14, 5)
assert tpi_algo.on_percent == 0.4
assert tpi_algo.calculated_on_percent == 0.4
assert tpi_algo.on_time_sec == 120
assert tpi_algo.off_time_sec == 180
# Test minimal activation delay
tpi_algo.calculate(15, 14.7, 15)
assert tpi_algo.on_percent == 0.09
assert tpi_algo.calculated_on_percent == 0.09
assert tpi_algo.on_time_sec == 0
assert tpi_algo.off_time_sec == 300
tpi_algo.set_security(0.09)
tpi_algo.calculate(15, 14.7, 15)
assert tpi_algo.on_percent == 0.09
assert tpi_algo.calculated_on_percent == 0.09
assert tpi_algo.on_time_sec == 0
assert tpi_algo.off_time_sec == 300