Testus
This commit is contained in:
@@ -546,6 +546,14 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
self._async_cancel_cycle()
|
self._async_cancel_cycle()
|
||||||
self._async_cancel_cycle = None
|
self._async_cancel_cycle = None
|
||||||
|
|
||||||
|
def find_underlying_climate(self, climate_entity_id) -> ClimateEntity:
|
||||||
|
"""Find the underlying climate entity"""
|
||||||
|
component: EntityComponent[ClimateEntity] = self.hass.data[CLIMATE_DOMAIN]
|
||||||
|
for entity in component.entities:
|
||||||
|
if climate_entity_id == entity.entity_id:
|
||||||
|
return entity
|
||||||
|
return None
|
||||||
|
|
||||||
async def async_startup(self):
|
async def async_startup(self):
|
||||||
"""Triggered on startup, used to get old state and set internal states accordingly"""
|
"""Triggered on startup, used to get old state and set internal states accordingly"""
|
||||||
_LOGGER.debug("%s - Calling async_startup", self)
|
_LOGGER.debug("%s - Calling async_startup", self)
|
||||||
@@ -557,19 +565,16 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
|
|
||||||
# Get the underlying thermostat
|
# Get the underlying thermostat
|
||||||
if self._is_over_climate:
|
if self._is_over_climate:
|
||||||
component: EntityComponent[ClimateEntity] = self.hass.data[
|
self._underlying_climate = self.find_underlying_climate(
|
||||||
CLIMATE_DOMAIN
|
self._climate_entity_id
|
||||||
]
|
)
|
||||||
for entity in component.entities:
|
if self._underlying_climate:
|
||||||
if self._climate_entity_id == entity.entity_id:
|
_LOGGER.info(
|
||||||
_LOGGER.info(
|
"%s - The underlying climate entity: %s have been succesfully found",
|
||||||
"%s - The underlying climate entity: %s have been succesfully found",
|
self,
|
||||||
self,
|
self._underlying_climate,
|
||||||
entity,
|
)
|
||||||
)
|
else:
|
||||||
self._underlying_climate = entity
|
|
||||||
break
|
|
||||||
if self._underlying_climate is None:
|
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"%s - Cannot find the underlying climate entity: %s. Thermostat will not be operational",
|
"%s - Cannot find the underlying climate entity: %s. Thermostat will not be operational",
|
||||||
self,
|
self,
|
||||||
@@ -924,7 +929,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return self.hass.states.is_state(self._heater_entity_id, STATE_ON)
|
return self._hass.states.is_state(self._heater_entity_id, STATE_ON)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_temperature(self):
|
def current_temperature(self):
|
||||||
|
|||||||
30
custom_components/versatile_thermostat/tests/commons.py
Normal file
30
custom_components/versatile_thermostat/tests/commons.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
""" Some common resources """
|
||||||
|
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.components.climate import ClimateEntity
|
||||||
|
from homeassistant.const import UnitOfTemperature
|
||||||
|
|
||||||
|
from homeassistant.components.climate import (
|
||||||
|
DOMAIN as CLIMATE_DOMAIN,
|
||||||
|
ATTR_PRESET_MODE,
|
||||||
|
HVACMode,
|
||||||
|
HVACAction,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MockClimate(ClimateEntity):
|
||||||
|
"""A Mock Climate class used for Underlying climate mode"""
|
||||||
|
|
||||||
|
def __init__(self, hass: HomeAssistant, unique_id, name, entry_infos) -> None:
|
||||||
|
"""Initialize the thermostat."""
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._hass = hass
|
||||||
|
self._attr_extra_state_attributes = {}
|
||||||
|
self._unique_id = unique_id
|
||||||
|
self._name = name
|
||||||
|
self._attr_hvac_action = HVACAction.OFF
|
||||||
|
self._attr_hvac_mode = HVACMode.OFF
|
||||||
|
self._attr_hvac_modes = [HVACMode.OFF, HVACMode.COOL, HVACMode.HEAT]
|
||||||
|
self._attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||||
@@ -18,10 +18,16 @@ from unittest.mock import patch
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.core import HomeAssistant, StateMachine
|
||||||
|
|
||||||
from custom_components.versatile_thermostat.config_flow import (
|
from custom_components.versatile_thermostat.config_flow import (
|
||||||
VersatileThermostatBaseConfigFlow,
|
VersatileThermostatBaseConfigFlow,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from custom_components.versatile_thermostat.climate import (
|
||||||
|
VersatileThermostat,
|
||||||
|
)
|
||||||
|
|
||||||
pytest_plugins = "pytest_homeassistant_custom_component"
|
pytest_plugins = "pytest_homeassistant_custom_component"
|
||||||
|
|
||||||
|
|
||||||
@@ -45,8 +51,30 @@ def skip_notifications_fixture():
|
|||||||
|
|
||||||
|
|
||||||
# This fixture is used to bypass the validate_input function in config_flow
|
# This fixture is used to bypass the validate_input function in config_flow
|
||||||
|
# NOT USED Now (keeped for memory)
|
||||||
@pytest.fixture(name="skip_validate_input")
|
@pytest.fixture(name="skip_validate_input")
|
||||||
def skip_validate_input_fixture():
|
def skip_validate_input_fixture():
|
||||||
"""Skip the validate_input in config flow"""
|
"""Skip the validate_input in config flow"""
|
||||||
with patch.object(VersatileThermostatBaseConfigFlow, "validate_input"):
|
with patch.object(VersatileThermostatBaseConfigFlow, "validate_input"):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="skip_hass_states_get")
|
||||||
|
def skip_hass_states_get_fixture():
|
||||||
|
"""Skip the get state in HomeAssistant"""
|
||||||
|
with patch.object(StateMachine, "get"):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="skip_hass_states_is_state")
|
||||||
|
def skip_hass_states_is_state_fixture():
|
||||||
|
"""Skip the is_state in HomeAssistant"""
|
||||||
|
with patch.object(StateMachine, "is_state", return_value=False):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="skip_send_event")
|
||||||
|
def skip_send_event_fixture():
|
||||||
|
"""Skip the send_event in VersatileThermostat"""
|
||||||
|
with patch.object(VersatileThermostat, "send_event"):
|
||||||
|
yield
|
||||||
|
|||||||
@@ -26,6 +26,19 @@ from custom_components.versatile_thermostat.const import (
|
|||||||
CONF_USE_MOTION_FEATURE,
|
CONF_USE_MOTION_FEATURE,
|
||||||
CONF_USE_POWER_FEATURE,
|
CONF_USE_POWER_FEATURE,
|
||||||
CONF_USE_PRESENCE_FEATURE,
|
CONF_USE_PRESENCE_FEATURE,
|
||||||
|
CONF_WINDOW_SENSOR,
|
||||||
|
CONF_WINDOW_DELAY,
|
||||||
|
CONF_MOTION_SENSOR,
|
||||||
|
CONF_MOTION_DELAY,
|
||||||
|
CONF_MOTION_PRESET,
|
||||||
|
CONF_NO_MOTION_PRESET,
|
||||||
|
CONF_POWER_SENSOR,
|
||||||
|
CONF_MAX_POWER_SENSOR,
|
||||||
|
CONF_DEVICE_POWER,
|
||||||
|
CONF_PRESET_POWER,
|
||||||
|
CONF_PRESENCE_SENSOR,
|
||||||
|
PRESET_AWAY_SUFFIX,
|
||||||
|
CONF_CLIMATE,
|
||||||
)
|
)
|
||||||
|
|
||||||
MOCK_TH_OVER_SWITCH_USER_CONFIG = {
|
MOCK_TH_OVER_SWITCH_USER_CONFIG = {
|
||||||
@@ -36,7 +49,21 @@ MOCK_TH_OVER_SWITCH_USER_CONFIG = {
|
|||||||
CONF_CYCLE_MIN: 5,
|
CONF_CYCLE_MIN: 5,
|
||||||
CONF_TEMP_MIN: 15,
|
CONF_TEMP_MIN: 15,
|
||||||
CONF_TEMP_MAX: 30,
|
CONF_TEMP_MAX: 30,
|
||||||
# Keep all additional optional features to false
|
CONF_USE_WINDOW_FEATURE: True,
|
||||||
|
CONF_USE_MOTION_FEATURE: True,
|
||||||
|
CONF_USE_POWER_FEATURE: True,
|
||||||
|
CONF_USE_PRESENCE_FEATURE: True,
|
||||||
|
}
|
||||||
|
|
||||||
|
MOCK_TH_OVER_CLIMATE_USER_CONFIG = {
|
||||||
|
CONF_NAME: "TheOverClimateMockName",
|
||||||
|
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_CLIMATE,
|
||||||
|
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
|
||||||
|
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
|
||||||
|
CONF_CYCLE_MIN: 5,
|
||||||
|
CONF_TEMP_MIN: 15,
|
||||||
|
CONF_TEMP_MAX: 30,
|
||||||
|
# Keep default values which are False
|
||||||
}
|
}
|
||||||
|
|
||||||
MOCK_TH_OVER_SWITCH_TYPE_CONFIG = {
|
MOCK_TH_OVER_SWITCH_TYPE_CONFIG = {
|
||||||
@@ -49,6 +76,9 @@ MOCK_TH_OVER_SWITCH_TPI_CONFIG = {
|
|||||||
CONF_TPI_COEF_EXT: 0.1,
|
CONF_TPI_COEF_EXT: 0.1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MOCK_TH_OVER_CLIMATE_TYPE_CONFIG = {
|
||||||
|
CONF_CLIMATE: "climate.mock_climate",
|
||||||
|
}
|
||||||
|
|
||||||
MOCK_PRESETS_CONFIG = {
|
MOCK_PRESETS_CONFIG = {
|
||||||
PRESET_ECO + "_temp": 16,
|
PRESET_ECO + "_temp": 16,
|
||||||
@@ -56,6 +86,32 @@ MOCK_PRESETS_CONFIG = {
|
|||||||
PRESET_BOOST + "_temp": 18,
|
PRESET_BOOST + "_temp": 18,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MOCK_WINDOW_CONFIG = {
|
||||||
|
CONF_WINDOW_SENSOR: "binary_sensor.window_sensor",
|
||||||
|
CONF_WINDOW_DELAY: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
MOCK_MOTION_CONFIG = {
|
||||||
|
CONF_MOTION_SENSOR: "input_boolean.motion_sensor",
|
||||||
|
CONF_MOTION_DELAY: 10,
|
||||||
|
CONF_MOTION_PRESET: PRESET_COMFORT,
|
||||||
|
CONF_NO_MOTION_PRESET: PRESET_ECO,
|
||||||
|
}
|
||||||
|
|
||||||
|
MOCK_POWER_CONFIG = {
|
||||||
|
CONF_POWER_SENSOR: "sensor.power_sensor",
|
||||||
|
CONF_MAX_POWER_SENSOR: "sensor.power_max_sensor",
|
||||||
|
CONF_DEVICE_POWER: 1,
|
||||||
|
CONF_PRESET_POWER: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
MOCK_PRESENCE_CONFIG = {
|
||||||
|
CONF_PRESENCE_SENSOR: "person.presence_sensor",
|
||||||
|
PRESET_ECO + PRESET_AWAY_SUFFIX + "_temp": 16,
|
||||||
|
PRESET_COMFORT + PRESET_AWAY_SUFFIX + "_temp": 17,
|
||||||
|
PRESET_BOOST + PRESET_AWAY_SUFFIX + "_temp": 18,
|
||||||
|
}
|
||||||
|
|
||||||
MOCK_ADVANCED_CONFIG = {
|
MOCK_ADVANCED_CONFIG = {
|
||||||
CONF_MINIMAL_ACTIVATION_DELAY: 10,
|
CONF_MINIMAL_ACTIVATION_DELAY: 10,
|
||||||
CONF_SECURITY_DELAY_MIN: 5,
|
CONF_SECURITY_DELAY_MIN: 5,
|
||||||
|
|||||||
@@ -10,11 +10,17 @@ from pytest_homeassistant_custom_component.common import MockConfigEntry, load_f
|
|||||||
from custom_components.versatile_thermostat.const import DOMAIN
|
from custom_components.versatile_thermostat.const import DOMAIN
|
||||||
from custom_components.versatile_thermostat import VersatileThermostatAPI
|
from custom_components.versatile_thermostat import VersatileThermostatAPI
|
||||||
|
|
||||||
from custom_components.versatile_thermostat.tests.const import (
|
from .const import (
|
||||||
MOCK_TH_OVER_SWITCH_USER_CONFIG,
|
MOCK_TH_OVER_SWITCH_USER_CONFIG,
|
||||||
|
MOCK_TH_OVER_CLIMATE_USER_CONFIG,
|
||||||
MOCK_TH_OVER_SWITCH_TYPE_CONFIG,
|
MOCK_TH_OVER_SWITCH_TYPE_CONFIG,
|
||||||
|
MOCK_TH_OVER_CLIMATE_TYPE_CONFIG,
|
||||||
MOCK_TH_OVER_SWITCH_TPI_CONFIG,
|
MOCK_TH_OVER_SWITCH_TPI_CONFIG,
|
||||||
MOCK_PRESETS_CONFIG,
|
MOCK_PRESETS_CONFIG,
|
||||||
|
MOCK_WINDOW_CONFIG,
|
||||||
|
MOCK_MOTION_CONFIG,
|
||||||
|
MOCK_POWER_CONFIG,
|
||||||
|
MOCK_PRESENCE_CONFIG,
|
||||||
MOCK_ADVANCED_CONFIG,
|
MOCK_ADVANCED_CONFIG,
|
||||||
MOCK_DEFAULT_FEATURE_CONFIG,
|
MOCK_DEFAULT_FEATURE_CONFIG,
|
||||||
)
|
)
|
||||||
@@ -35,8 +41,8 @@ async def test_show_form(hass: HomeAssistant) -> None:
|
|||||||
assert result["step_id"] == SOURCE_USER
|
assert result["step_id"] == SOURCE_USER
|
||||||
|
|
||||||
|
|
||||||
async def test_user_config_flow_over_switch(hass, skip_validate_input):
|
async def test_user_config_flow_over_switch(hass, skip_hass_states_get):
|
||||||
"""Test the config flow with thermostat_over_switch features"""
|
"""Test the config flow with all thermostat_over_switch features"""
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN, context={"source": SOURCE_USER}
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
)
|
)
|
||||||
@@ -72,6 +78,38 @@ async def test_user_config_flow_over_switch(hass, skip_validate_input):
|
|||||||
result["flow_id"], user_input=MOCK_PRESETS_CONFIG
|
result["flow_id"], user_input=MOCK_PRESETS_CONFIG
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "window"
|
||||||
|
assert result["errors"] == {}
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], user_input=MOCK_WINDOW_CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "motion"
|
||||||
|
assert result["errors"] == {}
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], user_input=MOCK_MOTION_CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "power"
|
||||||
|
assert result["errors"] == {}
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], user_input=MOCK_POWER_CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "presence"
|
||||||
|
assert result["errors"] == {}
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], user_input=MOCK_PRESENCE_CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
assert result["step_id"] == "advanced"
|
assert result["step_id"] == "advanced"
|
||||||
assert result["errors"] == {}
|
assert result["errors"] == {}
|
||||||
@@ -87,11 +125,99 @@ async def test_user_config_flow_over_switch(hass, skip_validate_input):
|
|||||||
| MOCK_TH_OVER_SWITCH_TYPE_CONFIG
|
| MOCK_TH_OVER_SWITCH_TYPE_CONFIG
|
||||||
| MOCK_TH_OVER_SWITCH_TPI_CONFIG
|
| MOCK_TH_OVER_SWITCH_TPI_CONFIG
|
||||||
| MOCK_PRESETS_CONFIG
|
| MOCK_PRESETS_CONFIG
|
||||||
|
| MOCK_WINDOW_CONFIG
|
||||||
|
| MOCK_MOTION_CONFIG
|
||||||
|
| MOCK_POWER_CONFIG
|
||||||
|
| MOCK_PRESENCE_CONFIG
|
||||||
| MOCK_ADVANCED_CONFIG
|
| MOCK_ADVANCED_CONFIG
|
||||||
| MOCK_DEFAULT_FEATURE_CONFIG
|
|
||||||
)
|
)
|
||||||
assert result["result"]
|
assert result["result"]
|
||||||
assert result["result"].domain == DOMAIN
|
assert result["result"].domain == DOMAIN
|
||||||
assert result["result"].version == 1
|
assert result["result"].version == 1
|
||||||
assert result["result"].title == "TheOverSwitchMockName"
|
assert result["result"].title == "TheOverSwitchMockName"
|
||||||
assert isinstance(result["result"], ConfigEntry)
|
assert isinstance(result["result"], ConfigEntry)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_user_config_flow_over_climate(hass, skip_hass_states_get):
|
||||||
|
"""Test the config flow with all thermostat_over_climate features and no additional features"""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == SOURCE_USER
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], user_input=MOCK_TH_OVER_CLIMATE_USER_CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "type"
|
||||||
|
assert result["errors"] == {}
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], user_input=MOCK_TH_OVER_CLIMATE_TYPE_CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "presets"
|
||||||
|
assert result["errors"] == {}
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], user_input=MOCK_PRESETS_CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
|
# assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
# assert result["step_id"] == "window"
|
||||||
|
# assert result["errors"] == {}
|
||||||
|
|
||||||
|
# result = await hass.config_entries.flow.async_configure(
|
||||||
|
# result["flow_id"], user_input=MOCK_WINDOW_CONFIG
|
||||||
|
# )
|
||||||
|
|
||||||
|
# assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
# assert result["step_id"] == "motion"
|
||||||
|
# assert result["errors"] == {}
|
||||||
|
|
||||||
|
# result = await hass.config_entries.flow.async_configure(
|
||||||
|
# result["flow_id"], user_input=MOCK_MOTION_CONFIG
|
||||||
|
# )
|
||||||
|
|
||||||
|
# assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
# assert result["step_id"] == "power"
|
||||||
|
# assert result["errors"] == {}
|
||||||
|
|
||||||
|
# result = await hass.config_entries.flow.async_configure(
|
||||||
|
# result["flow_id"], user_input=MOCK_POWER_CONFIG
|
||||||
|
# )
|
||||||
|
|
||||||
|
# assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
# assert result["step_id"] == "presence"
|
||||||
|
# assert result["errors"] == {}
|
||||||
|
|
||||||
|
# result = await hass.config_entries.flow.async_configure(
|
||||||
|
# result["flow_id"], user_input=MOCK_PRESENCE_CONFIG
|
||||||
|
# )
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "advanced"
|
||||||
|
assert result["errors"] == {}
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], user_input=MOCK_ADVANCED_CONFIG
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert (
|
||||||
|
result["data"]
|
||||||
|
== MOCK_TH_OVER_CLIMATE_USER_CONFIG
|
||||||
|
| MOCK_TH_OVER_CLIMATE_TYPE_CONFIG
|
||||||
|
| MOCK_PRESETS_CONFIG
|
||||||
|
| MOCK_ADVANCED_CONFIG
|
||||||
|
| MOCK_DEFAULT_FEATURE_CONFIG
|
||||||
|
)
|
||||||
|
assert result["result"]
|
||||||
|
assert result["result"].domain == DOMAIN
|
||||||
|
assert result["result"].version == 1
|
||||||
|
assert result["result"].title == "TheOverClimateMockName"
|
||||||
|
assert isinstance(result["result"], ConfigEntry)
|
||||||
|
|||||||
169
custom_components/versatile_thermostat/tests/test_start.py
Normal file
169
custom_components/versatile_thermostat/tests/test_start.py
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
""" Test the normal start of a Thermostat """
|
||||||
|
from unittest.mock import patch, call
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.components.climate import HVACAction, HVACMode
|
||||||
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
|
|
||||||
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
|
from homeassistant.components.climate import ClimateEntity, DOMAIN as CLIMATE_DOMAIN
|
||||||
|
|
||||||
|
from pytest_homeassistant_custom_component.common import MockConfigEntry
|
||||||
|
|
||||||
|
from ..climate import VersatileThermostat
|
||||||
|
|
||||||
|
from ..const import DOMAIN, EventType
|
||||||
|
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
|
||||||
|
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):
|
||||||
|
"""Test the normal full start of a thermostat in thermostat_over_switch type"""
|
||||||
|
|
||||||
|
entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
title="TheOverSwitchMockName",
|
||||||
|
unique_id="uniqueId",
|
||||||
|
data=FULL_SWITCH_CONFIG,
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"custom_components.versatile_thermostat.climate.VersatileThermostat.send_event"
|
||||||
|
) as mock_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("climate.theoverswitchmockname")
|
||||||
|
|
||||||
|
assert entity
|
||||||
|
|
||||||
|
assert entity.name == "TheOverSwitchMockName"
|
||||||
|
assert entity._is_over_climate is False
|
||||||
|
assert entity.hvac_action is HVACAction.OFF
|
||||||
|
assert entity.hvac_mode is HVACMode.OFF
|
||||||
|
assert entity.target_temperature == entity.min_temp
|
||||||
|
assert entity.preset_mode is None
|
||||||
|
assert entity._security_state is False
|
||||||
|
assert entity._window_state is None
|
||||||
|
assert entity._motion_state is None
|
||||||
|
assert entity._presence_state is None
|
||||||
|
|
||||||
|
# should have been called with EventType.PRESET_EVENT and EventType.HVAC_MODE_EVENT
|
||||||
|
assert mock_send_event.call_count == 2
|
||||||
|
|
||||||
|
# Impossible to make this work, but it works...
|
||||||
|
# assert mock_send_event.assert_has_calls(
|
||||||
|
# [
|
||||||
|
# call.send_event(EventType.PRESET_EVENT, {"preset": None}),
|
||||||
|
# call.send_event(
|
||||||
|
# EventType.HVAC_MODE_EVENT,
|
||||||
|
# {"hvac_mode": HVACMode.OFF},
|
||||||
|
# ),
|
||||||
|
# ]
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
|
async def test_over_climate_full_start(hass: HomeAssistant, skip_hass_states_is_state):
|
||||||
|
"""Test the normal full start of a thermostat in thermostat_over_climate type"""
|
||||||
|
|
||||||
|
entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
title="TheOverClimateMockName",
|
||||||
|
unique_id="uniqueId",
|
||||||
|
data=PARTIAL_CLIMATE_CONFIG,
|
||||||
|
)
|
||||||
|
|
||||||
|
fake_underlying_climate = MockClimate(hass, "mockUniqueId", "MockClimateName", {})
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"custom_components.versatile_thermostat.climate.VersatileThermostat.send_event"
|
||||||
|
) as mock_send_event, patch(
|
||||||
|
"custom_components.versatile_thermostat.climate.VersatileThermostat.find_underlying_climate",
|
||||||
|
return_value=fake_underlying_climate,
|
||||||
|
) as mock_find_climate:
|
||||||
|
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("climate.theoverclimatemockname")
|
||||||
|
|
||||||
|
assert entity
|
||||||
|
|
||||||
|
assert entity.name == "TheOverClimateMockName"
|
||||||
|
assert entity._is_over_climate is True
|
||||||
|
assert entity.hvac_action is HVACAction.OFF
|
||||||
|
assert entity.hvac_mode is HVACMode.OFF
|
||||||
|
assert entity.target_temperature == entity.min_temp
|
||||||
|
assert entity.preset_mode is None
|
||||||
|
assert entity._security_state is False
|
||||||
|
assert entity._window_state is None
|
||||||
|
assert entity._motion_state is None
|
||||||
|
assert entity._presence_state is None
|
||||||
|
|
||||||
|
# should have been called with EventType.PRESET_EVENT and EventType.HVAC_MODE_EVENT
|
||||||
|
assert mock_send_event.call_count == 2
|
||||||
|
mock_send_event.assert_has_calls(
|
||||||
|
[
|
||||||
|
call.send_event(EventType.PRESET_EVENT, {"preset": None}),
|
||||||
|
call.send_event(
|
||||||
|
EventType.HVAC_MODE_EVENT,
|
||||||
|
{"hvac_mode": HVACMode.OFF},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
assert mock_find_climate.call_count == 1
|
||||||
|
assert mock_find_climate.mock_calls[0] == call("climate.mock_climate")
|
||||||
|
mock_find_climate.assert_has_calls(
|
||||||
|
[call.find_underlying_entity("climate.mock_climate")]
|
||||||
|
)
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import unittest # The test framework
|
|
||||||
|
|
||||||
|
|
||||||
class Test_TestIncrementDecrement(unittest.TestCase):
|
|
||||||
def test_increment(self):
|
|
||||||
self.assertEqual(4, 4)
|
|
||||||
|
|
||||||
# This test is designed to fail for demonstration purposes.
|
|
||||||
def test_decrement(self):
|
|
||||||
self.assertEqual(3, 3)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
||||||
Reference in New Issue
Block a user