Testus 2
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
81
custom_components/versatile_thermostat/tests/test_tpi.py
Normal file
81
custom_components/versatile_thermostat/tests/test_tpi.py
Normal 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
|
||||||
Reference in New Issue
Block a user