Compare commits

...

5 Commits

Author SHA1 Message Date
Jean-Marc Collin 64ce3aa0ad Issue #81 - recursive loop when security should be set 2023-04-28 12:12:38 +02:00
Jean-Marc Collin 3f498ffbd3 Add BuyMeCoffee contributors (fr) 2023-04-26 08:23:03 +02:00
Jean-Marc Collin 3236be6c3b Update README.md
Add BuyMeCoffee contributors
2023-04-26 08:21:54 +02:00
Jean-Marc Collin be86fd3ac0 Merge branch 'main' of github.com:jmcollin78/versatile_thermostat into main 2023-04-22 11:06:12 +02:00
Jean-Marc Collin e35ba57bd7 Fix compilation warnings 2023-04-22 11:05:38 +02:00
7 changed files with 35 additions and 22 deletions
+1 -1
View File
@@ -62,7 +62,7 @@ Ce composant personnalisé pour Home Assistant est une mise à niveau et est une
> * **release majeure 2.0** : ajout du thermostat "over climate" permettant de transformer n'importe quel thermostat en Versatile Thermostat et lui ajouter toutes les fonctions de ce dernier. > * **release majeure 2.0** : ajout du thermostat "over climate" permettant de transformer n'importe quel thermostat en Versatile Thermostat et lui ajouter toutes les fonctions de ce dernier.
# Merci pour la bière [buymecoffee](https://www.buymeacoffee.com/jmcollin78) # Merci pour la bière [buymecoffee](https://www.buymeacoffee.com/jmcollin78)
Un grand merci à @salabur pour la bière. Ca fait très plaisir. Un grand merci à @salabur, @pvince83 and @bergoglio pour les bières. Ca fait très plaisir.
# Quand l'utiliser et ne pas l'utiliser # Quand l'utiliser et ne pas l'utiliser
+2 -1
View File
@@ -61,7 +61,8 @@ This custom component for Home Assistant is an upgrade and is a complete rewrite
> * **major release 2.0**: addition of the "over climate" thermostat allowing you to transform any thermostat into a Versatile Thermostat and add all the functions of the latter. > * **major release 2.0**: addition of the "over climate" thermostat allowing you to transform any thermostat into a Versatile Thermostat and add all the functions of the latter.
# Thanks for the beer [buymecoffee](https://www.buymeacoffee.com/jmcollin78) # Thanks for the beer [buymecoffee](https://www.buymeacoffee.com/jmcollin78)
Many thanks to @salabur for the beer. It's very pleasing. (Vielen Dank an @salabur für das Bier. Es ist sehr erfreulich.) Many thanks to @salabur, @pvince83 and @bergoglio for the beers. It's very pleasing.
# When to use / not use # When to use / not use
This thermostat can control 2 types of equipment: This thermostat can control 2 types of equipment:
@@ -1191,7 +1191,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
raise NotImplementedError() raise NotImplementedError()
async def async_set_hvac_mode(self, hvac_mode): async def async_set_hvac_mode(self, hvac_mode, need_control_heating=True):
"""Set new target hvac mode.""" """Set new target hvac mode."""
_LOGGER.info("%s - Set hvac mode: %s", self, hvac_mode) _LOGGER.info("%s - Set hvac mode: %s", self, hvac_mode)
@@ -1201,13 +1201,13 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
self._hvac_mode = hvac_mode self._hvac_mode = hvac_mode
# Delegate to all underlying # Delegate to all underlying
need_control_heating = False sub_need_control_heating = False
for under in self._underlyings: for under in self._underlyings:
need_control_heating = ( sub_need_control_heating = (
await under.set_hvac_mode(hvac_mode) or need_control_heating await under.set_hvac_mode(hvac_mode) or need_control_heating
) )
if need_control_heating: if need_control_heating and sub_need_control_heating:
await self._async_control_heating(force=True) await self._async_control_heating(force=True)
# Ensure we update the current operation after changing the mode # Ensure we update the current operation after changing the mode
@@ -1452,7 +1452,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
self, self,
self._saved_hvac_mode, self._saved_hvac_mode,
) )
await self.restore_hvac_mode() await self.restore_hvac_mode(True)
elif self._window_state == STATE_ON: elif self._window_state == STATE_ON:
_LOGGER.info( _LOGGER.info(
"%s - Window is open. Set hvac_mode to '%s'", self, HVACMode.OFF "%s - Window is open. Set hvac_mode to '%s'", self, HVACMode.OFF
@@ -1578,7 +1578,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
) )
_LOGGER.info( _LOGGER.info(
"%s - Underlying climate changed. Event.new_state is %s, hvac_mode=%s, hvac_action=%s, old_hvac_action=%s", "%s - Underlying climate changed. Event.new_state is %s, current_hvac_mode=%s, new_hvac_action=%s, old_hvac_action=%s",
self, self,
new_state, new_state,
self._hvac_mode, self._hvac_mode,
@@ -1857,7 +1857,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
) )
# Set attributes # Set attributes
self._window_auto_state = False self._window_auto_state = False
await self.restore_hvac_mode() await self.restore_hvac_mode(True)
if self._window_call_cancel: if self._window_call_cancel:
self._window_call_cancel() self._window_call_cancel()
@@ -1953,9 +1953,9 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
self._hvac_mode, self._hvac_mode,
) )
async def restore_hvac_mode(self): async def restore_hvac_mode(self, need_control_heating=False):
"""Restore a previous hvac_mod""" """Restore a previous hvac_mod"""
await self.async_set_hvac_mode(self._saved_hvac_mode) await self.async_set_hvac_mode(self._saved_hvac_mode, need_control_heating)
_LOGGER.debug( _LOGGER.debug(
"%s - Restored hvac_mode - saved_hvac_mode is %s, hvac_mode is %s", "%s - Restored hvac_mode - saved_hvac_mode is %s, hvac_mode is %s",
self, self,
@@ -2025,7 +2025,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
self._saved_preset_mode, self._saved_preset_mode,
) )
if self._is_over_climate: if self._is_over_climate:
await self.restore_hvac_mode() await self.restore_hvac_mode(False)
await self.restore_preset_mode() await self.restore_preset_mode()
self.send_event( self.send_event(
EventType.POWER_EVENT, EventType.POWER_EVENT,
@@ -2127,7 +2127,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
await self._async_set_preset_mode_internal(PRESET_SECURITY) await self._async_set_preset_mode_internal(PRESET_SECURITY)
# Turn off the underlying climate or heater if security default on_percent is 0 # Turn off the underlying climate or heater if security default on_percent is 0
if self._is_over_climate or self._security_default_on_percent <= 0.0: if self._is_over_climate or self._security_default_on_percent <= 0.0:
await self.async_set_hvac_mode(HVACMode.OFF) await self.async_set_hvac_mode(HVACMode.OFF, False)
if self._prop_algorithm: if self._prop_algorithm:
self._prop_algorithm.set_security(self._security_default_on_percent) self._prop_algorithm.set_security(self._security_default_on_percent)
@@ -2161,7 +2161,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
self._security_state = ret self._security_state = ret
# Restore hvac_mode if previously saved # Restore hvac_mode if previously saved
if self._is_over_climate or self._security_default_on_percent <= 0.0: if self._is_over_climate or self._security_default_on_percent <= 0.0:
await self.restore_hvac_mode() await self.restore_hvac_mode(False)
await self.restore_preset_mode() await self.restore_preset_mode()
if self._prop_algorithm: if self._prop_algorithm:
self._prop_algorithm.unset_security() self._prop_algorithm.unset_security()
@@ -2294,6 +2294,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
self.get_preset_away_name(PRESET_COMFORT) self.get_preset_away_name(PRESET_COMFORT)
), ),
"power_temp": self._power_temp, "power_temp": self._power_temp,
"target_temp": self.target_temperature,
"current_temp": self._cur_temp,
"ext_current_temperature": self._cur_ext_temp, "ext_current_temperature": self._cur_ext_temp,
"current_power": self._current_power, "current_power": self._current_power,
"current_power_max": self._current_power_max, "current_power_max": self._current_power_max,
@@ -1 +1,2 @@
homeassistant homeassistant
ffmpeg
@@ -2,7 +2,7 @@
import asyncio import asyncio
import logging import logging
from unittest.mock import patch, MagicMock from unittest.mock import patch, MagicMock
import pytest import pytest # pylint: disable=unused-import
from homeassistant.core import HomeAssistant, Event, EVENT_STATE_CHANGED, State from homeassistant.core import HomeAssistant, Event, EVENT_STATE_CHANGED, State
from homeassistant.const import UnitOfTemperature, STATE_ON, STATE_OFF from homeassistant.const import UnitOfTemperature, STATE_ON, STATE_OFF
@@ -9,7 +9,7 @@ from homeassistant.components.binary_sensor import BinarySensorDeviceClass
from pytest_homeassistant_custom_component.common import MockConfigEntry from pytest_homeassistant_custom_component.common import MockConfigEntry
from .commons import * from .commons import * # pylint: disable=wildcard-import, unused-wildcard-import
from ..climate import VersatileThermostat from ..climate import VersatileThermostat
from ..binary_sensor import ( from ..binary_sensor import (
SecurityBinarySensor, SecurityBinarySensor,
@@ -167,6 +167,7 @@ class UnderlyingSwitch(UnderlyingEntity):
self._should_relaunch_control_heating = False self._should_relaunch_control_heating = False
self._on_time_sec = 0 self._on_time_sec = 0
self._off_time_sec = 0 self._off_time_sec = 0
self._hvac_mode = None
@property @property
def initial_delay_sec(self): def initial_delay_sec(self):
@@ -174,12 +175,18 @@ class UnderlyingSwitch(UnderlyingEntity):
return self._initial_delay_sec return self._initial_delay_sec
async def set_hvac_mode(self, hvac_mode: HVACMode) -> bool: async def set_hvac_mode(self, hvac_mode: HVACMode) -> bool:
"""Set the HVACmode. Returns true if we need to redo a control_heating""" """Set the HVACmode. Returns true if something have change"""
if hvac_mode == HVACMode.OFF: if hvac_mode == HVACMode.OFF:
if self.is_device_active: if self.is_device_active:
await self.turn_off() await self.turn_off()
await self._cancel_cycle() await self._cancel_cycle()
return True
if self._hvac_mode != hvac_mode:
self._hvac_mode = hvac_mode
return True
else:
return False
@property @property
def is_device_active(self): def is_device_active(self):
@@ -421,10 +428,10 @@ class UnderlyingClimate(UnderlyingEntity):
"""True if the underlying climate was found""" """True if the underlying climate was found"""
return self._underlying_climate is not None return self._underlying_climate is not None
async def set_hvac_mode(self, hvac_mode: HVACMode): async def set_hvac_mode(self, hvac_mode: HVACMode) -> bool:
"""Set the HVACmode of the underlying climate""" """Set the HVACmode of the underlying climate. Returns true if something have change"""
if not self.is_initialized: if not self.is_initialized:
return return False
data = {ATTR_ENTITY_ID: self._entity_id, "hvac_mode": hvac_mode} data = {ATTR_ENTITY_ID: self._entity_id, "hvac_mode": hvac_mode}
await self._hass.services.async_call( await self._hass.services.async_call(
@@ -433,6 +440,8 @@ class UnderlyingClimate(UnderlyingEntity):
data, data,
) )
return True
@property @property
def is_device_active(self): def is_device_active(self):
"""If the toggleable device is currently active.""" """If the toggleable device is currently active."""