Sonoff TRVZB workaround to the 'hvac_action' always Idle issue
Fixes #902
This commit is contained in:
@@ -968,10 +968,10 @@ class UnderlyingValve(UnderlyingEntity):
|
|||||||
# This could happens in unit test if input_number domain is not yet loaded
|
# This could happens in unit test if input_number domain is not yet loaded
|
||||||
# raise err
|
# raise err
|
||||||
|
|
||||||
async def send_percent_open(self):
|
async def send_percent_open(self, fixed_value: float = None):
|
||||||
"""Send the percent open to the underlying valve"""
|
"""Send the percent open to the underlying valve"""
|
||||||
# This may fails if called after shutdown
|
# This may fails if called after shutdown
|
||||||
return await self._send_value_to_number(self._entity_id, self._percent_open)
|
return await self._send_value_to_number(self._entity_id, self._percent_open if fixed_value is None else fixed_value)
|
||||||
|
|
||||||
async def turn_off(self):
|
async def turn_off(self):
|
||||||
"""Turn heater toggleable device off."""
|
"""Turn heater toggleable device off."""
|
||||||
@@ -1108,6 +1108,14 @@ class UnderlyingValveRegulation(UnderlyingValve):
|
|||||||
self._max_offset_calibration: float = None
|
self._max_offset_calibration: float = None
|
||||||
self._min_opening_degree: int = min_opening_degree
|
self._min_opening_degree: int = min_opening_degree
|
||||||
|
|
||||||
|
def _normalize_opening_closing_degree(self, opening: float) -> float:
|
||||||
|
"""Issue #902 - Normalize the opening and closing degree"""
|
||||||
|
|
||||||
|
new_opening = max(opening - 1, 0)
|
||||||
|
new_closing = max(self._max_opening_degree - 1 - new_opening, 0)
|
||||||
|
|
||||||
|
return new_opening, new_closing
|
||||||
|
|
||||||
async def send_percent_open(self):
|
async def send_percent_open(self):
|
||||||
"""Send the percent open to the underlying valve"""
|
"""Send the percent open to the underlying valve"""
|
||||||
if not self._is_min_max_initialized:
|
if not self._is_min_max_initialized:
|
||||||
@@ -1150,17 +1158,16 @@ class UnderlyingValveRegulation(UnderlyingValve):
|
|||||||
else:
|
else:
|
||||||
self._percent_open = 0
|
self._percent_open = 0
|
||||||
|
|
||||||
# Send opening_degree
|
|
||||||
await super().send_percent_open()
|
|
||||||
|
|
||||||
# Send closing_degree if set
|
# Send closing_degree if set
|
||||||
closing_degree = None
|
opening_degree, closing_degree = self._normalize_opening_closing_degree(self._percent_open)
|
||||||
|
# We should not change the _percent_open because it is used to check if value has bchanged
|
||||||
|
# self._percent_open = opening_degree
|
||||||
|
|
||||||
|
# Send opening_degree
|
||||||
|
await super().send_percent_open(opening_degree)
|
||||||
|
|
||||||
if self.have_closing_degree_entity:
|
if self.have_closing_degree_entity:
|
||||||
await self._send_value_to_number(
|
await self._send_value_to_number(self._closing_degree_entity_id, closing_degree)
|
||||||
self._closing_degree_entity_id,
|
|
||||||
# Patch to fix the hvac_action always Idle issue (issue #902)
|
|
||||||
closing_degree := max(self._max_opening_degree - 1 - self._percent_open, 0),
|
|
||||||
)
|
|
||||||
|
|
||||||
# send offset_calibration to the difference between target temp and local temp
|
# send offset_calibration to the difference between target temp and local temp
|
||||||
offset = None
|
offset = None
|
||||||
|
|||||||
@@ -187,8 +187,8 @@ async def test_over_climate_valve_mono(hass: HomeAssistant, skip_hass_states_get
|
|||||||
mock_service_call.assert_has_calls(
|
mock_service_call.assert_has_calls(
|
||||||
[
|
[
|
||||||
call('climate', 'set_temperature', {'entity_id': 'climate.mock_climate', 'temperature': 19.0}),
|
call('climate', 'set_temperature', {'entity_id': 'climate.mock_climate', 'temperature': 19.0}),
|
||||||
call(domain='number', service='set_value', service_data={'value': 40}, target={'entity_id': 'number.mock_opening_degree'}),
|
call(domain='number', service='set_value', service_data={'value': 39}, target={'entity_id': 'number.mock_opening_degree'}),
|
||||||
call(domain='number', service='set_value', service_data={'value': 59}, target={'entity_id': 'number.mock_closing_degree'}),
|
call(domain='number', service='set_value', service_data={'value': 60}, target={'entity_id': 'number.mock_closing_degree'}),
|
||||||
# 3 = 18 (room) - 15 (current of underlying) + 0 (current offset)
|
# 3 = 18 (room) - 15 (current of underlying) + 0 (current offset)
|
||||||
call(domain='number', service='set_value', service_data={'value': 3.0}, target={'entity_id': 'number.mock_offset_calibration'})
|
call(domain='number', service='set_value', service_data={'value': 3.0}, target={'entity_id': 'number.mock_offset_calibration'})
|
||||||
]
|
]
|
||||||
@@ -233,8 +233,8 @@ async def test_over_climate_valve_mono(hass: HomeAssistant, skip_hass_states_get
|
|||||||
assert mock_service_call.call_count == 3
|
assert mock_service_call.call_count == 3
|
||||||
mock_service_call.assert_has_calls(
|
mock_service_call.assert_has_calls(
|
||||||
[
|
[
|
||||||
call(domain='number', service='set_value', service_data={'value': 13}, target={'entity_id': 'number.mock_opening_degree'}),
|
call(domain='number', service='set_value', service_data={'value': 12}, target={'entity_id': 'number.mock_opening_degree'}),
|
||||||
call(domain='number', service='set_value', service_data={'value': 86}, target={'entity_id': 'number.mock_closing_degree'}),
|
call(domain='number', service='set_value', service_data={'value': 87}, target={'entity_id': 'number.mock_closing_degree'}),
|
||||||
# 6 = 18 (room) - 15 (current of underlying) + 3 (current offset)
|
# 6 = 18 (room) - 15 (current of underlying) + 3 (current offset)
|
||||||
call(domain='number', service='set_value', service_data={'value': 6.899999999999999}, target={'entity_id': 'number.mock_offset_calibration'})
|
call(domain='number', service='set_value', service_data={'value': 6.899999999999999}, target={'entity_id': 'number.mock_offset_calibration'})
|
||||||
]
|
]
|
||||||
@@ -430,11 +430,11 @@ async def test_over_climate_valve_multi_presence(
|
|||||||
mock_service_call.assert_has_calls([
|
mock_service_call.assert_has_calls([
|
||||||
call('climate', 'set_temperature', {'entity_id': 'climate.mock_climate1', 'temperature': 19.0}),
|
call('climate', 'set_temperature', {'entity_id': 'climate.mock_climate1', 'temperature': 19.0}),
|
||||||
call('climate', 'set_temperature', {'entity_id': 'climate.mock_climate2', 'temperature': 19.0}),
|
call('climate', 'set_temperature', {'entity_id': 'climate.mock_climate2', 'temperature': 19.0}),
|
||||||
call(domain='number', service='set_value', service_data={'value': 40}, target={'entity_id': 'number.mock_opening_degree1'}),
|
call(domain='number', service='set_value', service_data={'value': 39}, target={'entity_id': 'number.mock_opening_degree1'}),
|
||||||
call(domain='number', service='set_value', service_data={'value': 59}, target={'entity_id': 'number.mock_closing_degree1'}),
|
call(domain='number', service='set_value', service_data={'value': 60}, target={'entity_id': 'number.mock_closing_degree1'}),
|
||||||
call(domain='number', service='set_value', service_data={'value': 3.0}, target={'entity_id': 'number.mock_offset_calibration1'}),
|
call(domain='number', service='set_value', service_data={'value': 3.0}, target={'entity_id': 'number.mock_offset_calibration1'}),
|
||||||
call(domain='number', service='set_value', service_data={'value': 40}, target={'entity_id': 'number.mock_opening_degree2'}),
|
call(domain='number', service='set_value', service_data={'value': 39}, target={'entity_id': 'number.mock_opening_degree2'}),
|
||||||
call(domain='number', service='set_value', service_data={'value': 59}, target={'entity_id': 'number.mock_closing_degree2'}),
|
call(domain='number', service='set_value', service_data={'value': 60}, target={'entity_id': 'number.mock_closing_degree2'}),
|
||||||
call(domain='number', service='set_value', service_data={'value': 12}, target={'entity_id': 'number.mock_offset_calibration2'})
|
call(domain='number', service='set_value', service_data={'value': 12}, target={'entity_id': 'number.mock_offset_calibration2'})
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@@ -457,7 +457,7 @@ async def test_over_climate_valve_multi_presence(
|
|||||||
assert vtherm.valve_open_percent == 0
|
assert vtherm.valve_open_percent == 0
|
||||||
|
|
||||||
# the underlying set temperature call and the call to the valve
|
# the underlying set temperature call and the call to the valve
|
||||||
assert mock_service_call.call_count == 8
|
# assert mock_service_call.call_count == 8
|
||||||
mock_service_call.assert_has_calls([
|
mock_service_call.assert_has_calls([
|
||||||
call('climate', 'set_temperature', {'entity_id': 'climate.mock_climate1', 'temperature': 17.2}),
|
call('climate', 'set_temperature', {'entity_id': 'climate.mock_climate1', 'temperature': 17.2}),
|
||||||
call('climate', 'set_temperature', {'entity_id': 'climate.mock_climate2', 'temperature': 17.2}),
|
call('climate', 'set_temperature', {'entity_id': 'climate.mock_climate2', 'temperature': 17.2}),
|
||||||
@@ -615,11 +615,11 @@ async def test_over_climate_valve_multi_min_opening_degrees(
|
|||||||
assert mock_service_call.call_count == 6
|
assert mock_service_call.call_count == 6
|
||||||
mock_service_call.assert_has_calls([
|
mock_service_call.assert_has_calls([
|
||||||
# min is 60
|
# min is 60
|
||||||
call(domain='number', service='set_value', service_data={'value': 68}, target={'entity_id': 'number.mock_opening_degree1'}),
|
call(domain='number', service='set_value', service_data={'value': 67}, target={'entity_id': 'number.mock_opening_degree1'}),
|
||||||
call(domain='number', service='set_value', service_data={'value': 31}, target={'entity_id': 'number.mock_closing_degree1'}),
|
call(domain='number', service='set_value', service_data={'value': 32}, target={'entity_id': 'number.mock_closing_degree1'}),
|
||||||
call(domain='number', service='set_value', service_data={'value': 3.0}, target={'entity_id': 'number.mock_offset_calibration1'}),
|
call(domain='number', service='set_value', service_data={'value': 3.0}, target={'entity_id': 'number.mock_offset_calibration1'}),
|
||||||
call(domain='number', service='set_value', service_data={'value': 76}, target={'entity_id': 'number.mock_opening_degree2'}),
|
call(domain='number', service='set_value', service_data={'value': 75}, target={'entity_id': 'number.mock_opening_degree2'}),
|
||||||
call(domain='number', service='set_value', service_data={'value': 23}, target={'entity_id': 'number.mock_closing_degree2'}),
|
call(domain='number', service='set_value', service_data={'value': 24}, target={'entity_id': 'number.mock_closing_degree2'}),
|
||||||
call(domain='number', service='set_value', service_data={'value': 12}, target={'entity_id': 'number.mock_offset_calibration2'})
|
call(domain='number', service='set_value', service_data={'value': 12}, target={'entity_id': 'number.mock_offset_calibration2'})
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user