Issue_766-enhance_power_management (#778)

* First implem + tests (not finished)

* With tests of calculate_shedding ok

* Commit for rebase

* All tests ok for central_feature_power_manager

* All tests not ok

* All tests ok

* integrattion tests - Do startup works

* enhance the overpowering algo if current_power > max_power

* Change shedding calculation delay to 20 sec (vs 60 sec)

* Integration tests ok

* Fix overpowering is set even if other heater have on_percent = 0

* Fix too much shedding in over_climate

* Add logs

* Add temporal filter for calculate_shedding
Add restore overpowering state at startup

* Fix restore overpowering_state

* Removes poweer_entity_id from vtherm non central config

* Release

* Add Sonoff TRVZB in creation.md

* Add comment on Sonoff TRVZB Closing degree

---------

Co-authored-by: Jean-Marc Collin <jean-marc.collin-extern@renault.com>
This commit is contained in:
Jean-Marc Collin
2025-01-05 18:10:18 +01:00
committed by GitHub
parent 9c8a965dba
commit 22b2b965c1
50 changed files with 1958 additions and 4055 deletions

View File

@@ -266,7 +266,9 @@ async def test_bug_272(
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@pytest.mark.parametrize("expected_lingering_timers", [True])
async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state):
async def test_bug_407(
hass: HomeAssistant, skip_hass_states_is_state, init_central_power_manager
):
"""Test the followin case in power management:
1. a heater is active (heating). So the power consumption takes the heater power into account. We suppose the power consumption is near the threshold,
2. the user switch preset let's say from Comfort to Boost,
@@ -275,6 +277,12 @@ async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state):
"""
temps = {
"eco": 17,
"comfort": 18,
"boost": 19,
}
entry = MockConfigEntry(
domain=DOMAIN,
title="TheOverSwitchMockName",
@@ -287,9 +295,6 @@ async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state):
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 15,
CONF_TEMP_MAX: 30,
"eco_temp": 17,
"comfort_temp": 18,
"boost_temp": 19,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: True,
@@ -301,34 +306,43 @@ async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state):
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SAFETY_DELAY_MIN: 5,
CONF_SAFETY_MIN_ON_PERCENT: 0.3,
CONF_POWER_SENSOR: "sensor.mock_power_sensor",
CONF_MAX_POWER_SENSOR: "sensor.mock_power_max_sensor",
CONF_DEVICE_POWER: 100,
CONF_PRESET_POWER: 12,
},
)
entity: ThermostatOverSwitch = await create_thermostat(
hass, entry, "climate.theoverswitchmockname"
hass, entry, "climate.theoverswitchmockname", temps
)
assert entity
tpi_algo = entity._prop_algorithm
assert tpi_algo
tz = get_tz(hass) # pylint: disable=invalid-name
now: datetime = datetime.now(tz=tz)
now: datetime = NowClass.get_now(hass)
VersatileThermostatAPI.get_vtherm_api()._set_now(now)
await send_temperature_change_event(entity, 16, now)
await send_ext_temperature_change_event(entity, 10, now)
# 1. An already active heater will not switch to overpowering
side_effects = SideEffects(
{
"sensor.the_power_sensor": State("sensor.the_power_sensor", 100),
"sensor.the_max_power_sensor": State("sensor.the_max_power_sensor", 110),
},
State("unknown.entity_id", "unknown"),
)
with patch(
"homeassistant.core.ServiceRegistry.async_call"
) as mock_service_call, patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
new_callable=PropertyMock,
return_value=True,
), patch(
"homeassistant.core.StateMachine.get",
side_effect=side_effects.get_side_effects(),
):
await entity.async_set_hvac_mode(HVACMode.HEAT)
await entity.async_set_preset_mode(PRESET_COMFORT)
@@ -337,16 +351,17 @@ async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state):
assert entity.power_manager.overpowering_state is STATE_UNKNOWN
assert entity.target_temperature == 18
# waits that the heater starts
await asyncio.sleep(0.1)
await hass.async_block_till_done()
assert mock_service_call.call_count >= 1
assert entity.is_device_active is True
# Send power max mesurement
await send_max_power_change_event(entity, 110, datetime.now())
await send_max_power_change_event(entity, 110, now)
# Send power mesurement (theheater is already in the power measurement)
await send_power_change_event(entity, 100, datetime.now())
await send_power_change_event(entity, 100, now)
# No overpowering yet
assert await entity.power_manager.check_overpowering() is False
assert entity.power_manager.is_overpowering_detected is False
# All configuration is complete and power is < power_max
assert entity.preset_mode is PRESET_COMFORT
assert entity.power_manager.overpowering_state is STATE_OFF
@@ -359,36 +374,57 @@ async def test_bug_407(hass: HomeAssistant, skip_hass_states_is_state):
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
new_callable=PropertyMock,
return_value=True,
), patch(
"homeassistant.core.StateMachine.get",
side_effect=side_effects.get_side_effects(),
):
now = now + timedelta(seconds=30)
VersatileThermostatAPI.get_vtherm_api()._set_now(now)
# change preset to Boost
await entity.async_set_preset_mode(PRESET_BOOST)
# waits that the heater starts
await asyncio.sleep(0.1)
# doesn't work for call_later
# await hass.async_block_till_done()
assert await entity.power_manager.check_overpowering() is False
# simulate a refresh for central power (not necessary)
await do_central_power_refresh(hass)
assert entity.power_manager.is_overpowering_detected is False
assert entity.hvac_mode is HVACMode.HEAT
assert entity.preset_mode is PRESET_BOOST
assert entity.power_manager.overpowering_state is STATE_OFF
assert entity.target_temperature == 19
assert mock_service_call.call_count >= 1
# 3. if heater is stopped (is_device_active==False), then overpowering should be started
# 3. if heater is stopped (is_device_active==False) and power is over max, then overpowering should be started
side_effects.add_or_update_side_effect("sensor.the_power_sensor", State("sensor.the_power_sensor", 150))
with patch(
"homeassistant.core.ServiceRegistry.async_call"
) as mock_service_call, patch(
"custom_components.versatile_thermostat.underlyings.UnderlyingSwitch.is_device_active",
new_callable=PropertyMock,
return_value=False,
), patch(
"homeassistant.core.StateMachine.get",
side_effect=side_effects.get_side_effects(),
):
now = now + timedelta(seconds=30)
VersatileThermostatAPI.get_vtherm_api()._set_now(now)
# change preset to Boost
await entity.async_set_preset_mode(PRESET_COMFORT)
# waits that the heater starts
await asyncio.sleep(0.1)
assert await entity.power_manager.check_overpowering() is True
# simulate a refresh for central power (not necessary)
await do_central_power_refresh(hass)
assert entity.power_manager.is_overpowering_detected is False
assert entity.hvac_mode is HVACMode.HEAT
assert entity.preset_mode is PRESET_POWER
assert entity.power_manager.overpowering_state is STATE_ON
assert entity.preset_mode is PRESET_COMFORT
assert entity.power_manager.overpowering_state is STATE_OFF
@pytest.mark.parametrize("expected_lingering_tasks", [True])
@@ -445,8 +481,6 @@ async def test_bug_500_3(hass: HomeAssistant, init_vtherm_api) -> None:
CONF_USE_WINDOW_CENTRAL_CONFIG: False,
CONF_WINDOW_SENSOR: "sensor.theWindowSensor",
CONF_USE_POWER_CENTRAL_CONFIG: False,
CONF_POWER_SENSOR: "sensor.thePowerSensor",
CONF_MAX_POWER_SENSOR: "sensor.theMaxPowerSensor",
CONF_USE_PRESENCE_CENTRAL_CONFIG: False,
CONF_PRESENCE_SENSOR: "sensor.thePresenceSensor",
CONF_USE_MOTION_FEATURE: True, # motion sensor need to be checked AND a motion sensor set
@@ -456,7 +490,7 @@ async def test_bug_500_3(hass: HomeAssistant, init_vtherm_api) -> None:
flow = VersatileThermostatBaseConfigFlow(config)
assert flow._infos[CONF_USE_WINDOW_FEATURE] is True
assert flow._infos[CONF_USE_POWER_FEATURE] is True
assert flow._infos[CONF_USE_POWER_FEATURE] is False
assert flow._infos[CONF_USE_PRESENCE_FEATURE] is True
assert flow._infos[CONF_USE_MOTION_FEATURE] is True