Files
versatile_thermostat/tests/test_open_window_algo.py
Jean-Marc Collin 23f9c7c52f Feature 181 & 242 - improve auto window detection (#243)
* Add ema calculation class, calculate an emo temperature, use the ema_temperature in auto_window dectection

* Removes circular dependency error

* Fix ema_temp unknown and remove slope smoothing

* 15 sec between two slope calculation

* Take Maia feedbacks on the algo.

* Maia comments: change MAX_ALPHA to 0.5, add slope calculation at each cycle.

* With EMA entity and slope calculation optimisations

* Change open_window_detection fake datapoint threshold

* Try auto window new algo

* Don't store datetime of fake datapoint

* Change auto window threshold in °/hour

---------

Co-authored-by: Jean-Marc Collin <jean-marc.collin-extern@renault.com>
2023-12-01 21:02:53 +01:00

241 lines
8.0 KiB
Python

# pylint: disable=unused-argument, line-too-long
""" Test the OpenWindow algorithm """
from datetime import datetime, timedelta
from custom_components.versatile_thermostat.open_window_algorithm import (
WindowOpenDetectionAlgorithm,
)
from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
async def test_open_window_algo(
hass: HomeAssistant,
skip_hass_states_is_state,
):
"""Tests the Algo"""
the_algo = WindowOpenDetectionAlgorithm(60.0, 0.0)
assert the_algo.last_slope is None
tz = get_tz(hass) # pylint: disable=invalid-name
now = datetime.now(tz)
event_timestamp = now - timedelta(minutes=10)
last_slope = the_algo.add_temp_measurement(
temperature=10, datetime_measure=event_timestamp
)
# We need at least 4 measurement
assert last_slope is None
assert the_algo.last_slope is None
assert the_algo.is_window_close_detected() is False
assert the_algo.is_window_open_detected() is False
event_timestamp = now - timedelta(minutes=9)
last_slope = the_algo.add_temp_measurement(
temperature=10, datetime_measure=event_timestamp
)
event_timestamp = now - timedelta(minutes=8)
last_slope = the_algo.add_temp_measurement(
temperature=10, datetime_measure=event_timestamp
)
event_timestamp = now - timedelta(minutes=7)
last_slope = the_algo.add_temp_measurement(
temperature=10, datetime_measure=event_timestamp
)
# No slope because same temperature
assert last_slope == 0
assert the_algo.last_slope == 0
assert the_algo.is_window_close_detected() is True
assert the_algo.is_window_open_detected() is False
event_timestamp = now - timedelta(minutes=6)
last_slope = the_algo.add_temp_measurement(
temperature=9, datetime_measure=event_timestamp
)
# A slope is calculated
assert last_slope == -48.0
assert the_algo.last_slope == -48.0
assert the_algo.is_window_close_detected() is False
assert the_algo.is_window_open_detected() is False
# A new temperature with 2 degre less in one minute (value will be rejected)
event_timestamp = now - timedelta(minutes=5)
last_slope = the_algo.add_temp_measurement(
temperature=7, datetime_measure=event_timestamp
)
# A slope is calculated
assert last_slope == (-48.0 * 0.2 - 120.0 * 0.8)
assert the_algo.last_slope == -105.6
assert the_algo.is_window_close_detected() is False
assert the_algo.is_window_open_detected() is True
# A new temperature with 1 degre less
event_timestamp = now - timedelta(minutes=4)
last_slope = the_algo.add_temp_measurement(
temperature=6, datetime_measure=event_timestamp
)
# A slope is calculated
assert last_slope == -105.6 * 0.2 - 60.0 * 0.8
assert the_algo.last_slope == -69.12
assert the_algo.is_window_close_detected() is False
assert the_algo.is_window_open_detected() is True
# A new temperature with 0 degre less
event_timestamp = now - timedelta(minutes=3)
last_slope = the_algo.add_temp_measurement(
temperature=6, datetime_measure=event_timestamp
)
# A slope is calculated
assert last_slope == round(-69.12 * 0.2 - 0.0 * 0.8, 2)
assert the_algo.last_slope == -13.82
assert the_algo.is_window_close_detected() is False
assert the_algo.is_window_open_detected() is False
# A new temperature with 1 degre more
event_timestamp = now - timedelta(minutes=2)
last_slope = the_algo.add_temp_measurement(
temperature=7, datetime_measure=event_timestamp
)
# A slope is calculated
assert last_slope == round(-13.82 * 0.2 + 60.0 * 0.8, 2)
assert the_algo.last_slope == 45.24
assert the_algo.is_window_close_detected() is True
assert the_algo.is_window_open_detected() is False
async def test_open_window_algo_wrong(
hass: HomeAssistant,
skip_hass_states_is_state,
):
"""Tests the Algo with wrong date"""
the_algo = WindowOpenDetectionAlgorithm(60.0, 0.0)
assert the_algo.last_slope is None
tz = get_tz(hass) # pylint: disable=invalid-name
now = datetime.now(tz)
event_timestamp = now - timedelta(minutes=5)
last_slope = the_algo.add_temp_measurement(
temperature=10, datetime_measure=event_timestamp
)
# We need at least 2 measurement
assert last_slope is None
assert the_algo.last_slope is None
assert the_algo.is_window_close_detected() is False
assert the_algo.is_window_open_detected() is False
# The next datetime_measurement cannot be in the past
event_timestamp = now - timedelta(minutes=6)
last_slope = the_algo.add_temp_measurement(
temperature=18, datetime_measure=event_timestamp
)
# No slope because same temperature
assert last_slope is None
assert the_algo.last_slope is None
assert the_algo.is_window_close_detected() is False
assert the_algo.is_window_open_detected() is False
async def test_open_window_algo_fake_point(
hass: HomeAssistant,
skip_hass_states_is_state,
):
"""Tests the Algo with adding fake point"""
the_algo = WindowOpenDetectionAlgorithm(3.0, 0.1)
assert the_algo.last_slope is None
tz = get_tz(hass) # pylint: disable=invalid-name
now = datetime.now(tz)
event_timestamp = now
last_slope = the_algo.check_age_last_measurement(
temperature=10, datetime_now=event_timestamp
)
# We need at least 4 measurement
assert last_slope is None
assert the_algo.last_slope is None
assert the_algo.is_window_close_detected() is False
assert the_algo.is_window_open_detected() is False
event_timestamp = now + timedelta(minutes=1)
last_slope = the_algo.add_temp_measurement(
temperature=10, datetime_measure=event_timestamp
)
event_timestamp = now + timedelta(minutes=2)
last_slope = the_algo.add_temp_measurement(
temperature=10, datetime_measure=event_timestamp
)
event_timestamp = now + timedelta(minutes=3)
last_slope = the_algo.add_temp_measurement(
temperature=10, datetime_measure=event_timestamp
)
# No slope because same temperature
assert last_slope == 0
assert the_algo.last_slope == 0
assert the_algo.is_window_close_detected() is False
assert the_algo.is_window_open_detected() is False
event_timestamp = now + timedelta(minutes=4)
last_slope = the_algo.add_temp_measurement(
temperature=9, datetime_measure=event_timestamp
)
# A slope is calculated
assert last_slope == -48.0
assert the_algo.last_slope == -48.0
assert the_algo.is_window_close_detected() is False
assert the_algo.is_window_open_detected() is True # One degre in one minute
# 1 Add a fake point one minute later
event_timestamp = now + timedelta(minutes=5)
last_slope = the_algo.check_age_last_measurement(
temperature=8, datetime_now=event_timestamp
)
# The slope not have change (fake point is ignored)
assert last_slope == -48.0
assert the_algo.last_slope == -48.0
assert the_algo.is_window_close_detected() is False
assert the_algo.is_window_open_detected() is True # One degre in one minute
# 2 Add a fake point 31 minute later -> +2 degres in 32 minutes
event_timestamp = event_timestamp + timedelta(minutes=31)
last_slope = the_algo.check_age_last_measurement(
temperature=10, datetime_now=event_timestamp
)
# The slope should have change (fake point is added)
assert last_slope == -8.1
assert the_algo.last_slope == -8.1
assert the_algo.is_window_close_detected() is False
assert the_algo.is_window_open_detected() is True
# 3 Add a 2nd fake point 30 minute later -> +3 degres in 30 minutes
event_timestamp = event_timestamp + timedelta(minutes=31)
last_slope = the_algo.check_age_last_measurement(
temperature=13, datetime_now=event_timestamp
)
# The slope should have change (fake point is added)
assert last_slope == 0.67
assert the_algo.last_slope == 0.67
assert the_algo.is_window_close_detected() is True
assert the_algo.is_window_open_detected() is False