async def _async_control_heating(self, time=None, force=False): """Check if we need to turn heating on or off.""" async with self._temp_lock: if not self._active and None not in (self._cur_temp, self._target_temp): self._active = True _LOGGER.info("Obtained current and target temperature. " "Generic thermostat active. %s, %s", self._cur_temp, self._target_temp) if not self._active or self._hvac_mode == HVAC_MODE_OFF: return if not force and time is None: # If the `force` argument is True, we # ignore `min_cycle_duration`. # If the `time` argument is not none, we were invoked for # keep-alive purposes, and `min_cycle_duration` is irrelevant. if self.min_cycle_duration: if self._is_device_active: current_state = STATE_ON else: current_state = HVAC_MODE_OFF if (self._hvac_mode == HVAC_MODE_COOL): long_enough = condition.state( self.hass, self.cooler_entity_id, current_state, self.min_cycle_duration) elif (self._hvac_mode == HVAC_MODE_HEAT): long_enough = condition.state( self.hass, self.heater_entity_id, current_state, self.min_cycle_duration) if not long_enough: return too_cold = \ self._target_temp - self._cur_temp >= self._tolerance too_hot = \ self._cur_temp - self._target_temp >= self._tolerance if self._is_device_active: if (self._hvac_mode == HVAC_MODE_COOL and too_cold) or (self._hvac_mode == HVAC_MODE_HEAT and too_hot): _LOGGER.info("Turning off HVAC") await self._async_turn_off() elif time is not None: # The time argument is passed only in keep-alive case if (self._hvac_mode == HVAC_MODE_COOL): await self._async_cooler_turn_on() elif (self._hvac_mode == HVAC_MODE_HEAT): await self._async_heater_turn_on() else: if (self._hvac_mode == HVAC_MODE_COOL and too_hot): _LOGGER.info("Turning on cooler %s", self.cooler_entity_id) await self._async_cooler_turn_on() elif (self._hvac_mode == HVAC_MODE_HEAT and too_cold): _LOGGER.info("Turning on heater %s", self.heater_entity_id) await self._async_heater_turn_on() elif time is not None: # The time argument is passed only in keep-alive case await self._async_turn_off()
async def test_state_raises(hass): """Test that state raises ConditionError on errors.""" # No entity with pytest.raises(ConditionError, match="no entity"): condition.state(hass, entity=None, req_state="missing") # Unknown entities test = await condition.async_from_config( hass, { "condition": "state", "entity_id": ["sensor.door_unknown", "sensor.window_unknown"], "state": "open", }, ) with pytest.raises(ConditionError, match="unknown entity.*door"): test(hass) with pytest.raises(ConditionError, match="unknown entity.*window"): test(hass) # Unknown attribute with pytest.raises(ConditionError, match=r"attribute .* does not exist"): test = await condition.async_from_config( hass, { "condition": "state", "entity_id": "sensor.door", "attribute": "model", "state": "acme", }, ) hass.states.async_set("sensor.door", "open") test(hass) # Unknown state entity with pytest.raises(ConditionError, match="input_text.missing"): test = await condition.async_from_config( hass, { "condition": "state", "entity_id": "sensor.door", "state": "input_text.missing", }, ) hass.states.async_set("sensor.door", "open") test(hass)
def test_is_state(hass: HomeAssistant, variables: TemplateVarsType) -> bool: """Test if an entity is a certain state.""" result = condition.state(hass, config[ATTR_ENTITY_ID], STATE_HOME) if reverse: result = not result return result
async def _async_control_humidification(self, time=None, force=False): """Check if we need to turn humidification on or off.""" async with self._humidity_lock: if not self._active and None not in (self._cur_humidity, self._target_humidity): self._active = True _LOGGER.info( "Obtained current and target humidity. " "Generic hygrostat active. %s, %s", self._cur_humidity, self._target_humidity, ) if not self._active or self._hvac_mode == HVAC_MODE_FAN_ONLY or self._hvac_mode == HVAC_MODE_OFF: return if not force and time is None: # If the `force` argument is True, we # ignore `min_cycle_duration`. # If the `time` argument is not none, we were invoked for # keep-alive purposes, and `min_cycle_duration` is irrelevant. if self.min_cycle_duration: if self._is_device_active: current_state = STATE_ON else: current_state = HVAC_MODE_OFF long_enough = condition.state( self.hass, self.dryer_entity_id, current_state, self.min_cycle_duration, ) if not long_enough: return too_dry = self._target_humidity >= self._cur_humidity + self._dry_tolerance too_moist = self._cur_humidity >= self._target_humidity + self._moist_tolerance if self._is_device_active: if (self.moist_mode and too_moist) or (not self.moist_mode and too_dry): _LOGGER.info("Turning off dryer %s", self.dryer_entity_id) await self._async_dryer_turn_off() elif time is not None: # The time argument is passed only in keep-alive case _LOGGER.info( "Keep-alive - Turning on dryer %s", self.dryer_entity_id, ) await self._async_dryer_turn_on() else: if (self.moist_mode and too_dry) or (not self.moist_mode and too_moist): _LOGGER.info("Turning on dryer %s", self.dryer_entity_id) await self._async_dryer_turn_on() elif time is not None: # The time argument is passed only in keep-alive case _LOGGER.info("Keep-alive - Turning off dryer %s", self.dryer_entity_id) await self._async_dryer_turn_off()
def _process_state(self, entity_observation): """Add entity to current observations if state conditions are met.""" entity = entity_observation['entity_id'] should_trigger = condition.state( self.hass, entity, entity_observation.get('to_state')) self._update_current_obs(entity_observation, should_trigger)
def is_active_long_enough(self, mode=None): """ This function is to check if the heater/cooler has been active long enough """ if not self.min_cycle_duration: return True if self._is_device_active: current_state = STATE_ON else: current_state = STATE_OFF if mode == "heat": for entity in self.heaters_entity_ids: return condition.state(self.hass, entity, current_state, self.min_cycle_duration) elif mode == "cool": for entity in self.coolers_entity_ids: return condition.state(self.hass, entity, current_state, self.min_cycle_duration) else: _LOGGER.error("Wrong mode have been passed to function is_active_long_enough") return True
def _process_state(self, entity_observation): """Return True if state conditions are met.""" entity = entity_observation["entity_id"] should_trigger = condition.state(self.hass, entity, entity_observation.get("to_state")) return should_trigger
def _process_state(self, entity_observation): """Add entity to current observations if state conditions are met.""" entity = entity_observation["entity_id"] should_trigger = condition.state(self.hass, entity, entity_observation.get("to_state")) self._update_current_obs(entity_observation, should_trigger)
def _process_state(self, entity_observation): """Return True if state conditions are met.""" entity = entity_observation["entity_id"] try: return condition.state(self.hass, entity, entity_observation.get("to_state")) except ConditionError: return False
def _async_control_heating(self): """Check if we need to turn heating on or off.""" if not self._active and None not in (self._cur_temp, self._target_temp): self._active = True _LOGGER.info( "Obtained current and target temperature. " "Generic thermostat active. %s, %s", self._cur_temp, self._target_temp) if not self._active: return if not self._enabled: return if self.min_cycle_duration: if self._is_device_active: current_state = STATE_ON else: current_state = STATE_OFF long_enough = condition.state(self.hass, self.heater_entity_id, current_state, self.min_cycle_duration) if not long_enough: return if self.ac_mode: is_cooling = self._is_device_active if is_cooling: too_cold = self._target_temp - self._cur_temp >= \ self._cold_tolerance if too_cold: _LOGGER.info("Turning off AC %s", self.heater_entity_id) self._heater_turn_off() else: too_hot = self._cur_temp - self._target_temp >= \ self._hot_tolerance if too_hot: _LOGGER.info("Turning on AC %s", self.heater_entity_id) self._heater_turn_on() else: is_heating = self._is_device_active if is_heating: too_hot = self._cur_temp - self._target_temp >= \ self._hot_tolerance if too_hot: _LOGGER.info("Turning off heater %s", self.heater_entity_id) self._heater_turn_off() else: too_cold = self._target_temp - self._cur_temp >= \ self._cold_tolerance if too_cold: _LOGGER.info("Turning on heater %s", self.heater_entity_id) self._heater_turn_on()
def test_is_state(hass: HomeAssistant, variables: TemplateVarsType): """ Test thumbnail state """ return condition.state( hass, entity_id, state, for_period, attribute, )
def _async_control_heating(self): """Check if we need to turn heating on or off.""" if not self._active and None not in (self._cur_temp, self._target_temp): self._active = True _LOGGER.info("Obtained current and target temperature. " "Generic thermostat active. %s, %s", self._cur_temp, self._target_temp) if not self._active: return if not self._enabled: return if self.min_cycle_duration: if self._is_device_active: current_state = STATE_ON else: current_state = STATE_OFF long_enough = condition.state( self.hass, self.heater_entity_id, current_state, self.min_cycle_duration) if not long_enough: return if self.ac_mode: is_cooling = self._is_device_active if is_cooling: too_cold = self._target_temp - self._cur_temp >= \ self._cold_tolerance if too_cold: _LOGGER.info("Turning off AC %s", self.heater_entity_id) self._heater_turn_off() else: too_hot = self._cur_temp - self._target_temp >= \ self._hot_tolerance if too_hot: _LOGGER.info("Turning on AC %s", self.heater_entity_id) self._heater_turn_on() else: is_heating = self._is_device_active if is_heating: too_hot = self._cur_temp - self._target_temp >= \ self._hot_tolerance if too_hot: _LOGGER.info("Turning off heater %s", self.heater_entity_id) self._heater_turn_off() else: too_cold = self._target_temp - self._cur_temp >= \ self._cold_tolerance if too_cold: _LOGGER.info("Turning on heater %s", self.heater_entity_id) self._heater_turn_on()
def test_is_state(hass: HomeAssistant, variables: TemplateVarsType) -> bool: """Test if an entity is a certain state.""" return condition.state( hass, config[ATTR_ENTITY_ID], "linked", attribute=config[CONF_TYPE][:-len( "_linked" )], # Condition names are attribute + _linked. Bit hacky, TODO: replace with removesuffix in Python 3.9 )
async def _async_control_heat_cool(self, time=None, force=False): """Check if we need to turn heating on or off.""" async with self._temp_lock: if not self._active and None not in ( self._cur_temp, self._target_temp_high, self._target_temp_low, ): self._active = True if not self._active or self._hvac_mode == HVAC_MODE_OFF: return if not force and time is None: # If the `force` argument is True, we # ignore `min_cycle_duration`. # If the `time` argument is not none, we were invoked for # keep-alive purposes, and `min_cycle_duration` is irrelevant. if self.min_cycle_duration: if self._is_device_active: current_state = STATE_ON else: current_state = HVAC_MODE_OFF long_enough = condition.state( self.hass, self.heater_entity_id, self.cooler_entity_id, current_state, self.min_cycle_duration, ) if not long_enough: return too_cold = self._target_temp_low >= self._cur_temp + self._cold_tolerance too_hot = self._cur_temp >= self._target_temp_high + self._hot_tolerance if self._is_opening_open: await self._async_heater_turn_off() await self._async_cooler_turn_off() elif self._is_floor_hot: await self._async_heater_turn_off() else: await self.async_heater_cooler_toggle(too_cold, too_hot) if time is not None: # The time argument is passed only in keep-alive case _LOGGER.info( "Keep-alive - Toggling on heater cooler %s, %s", self.heater_entity_id, self.cooler_entity_id, ) await self.async_heater_cooler_toggle(too_cold, too_hot)
async def _async_control_heating(self, time=None, force=False): """Check if we need to turn heating on or off.""" async with self._temp_lock: if not self._active and None not in (self._cur_temp, self._target_temp): self._active = True _LOGGER.info("Obtained current and target temperature. " "Generic thermostat active. %s, %s", self._cur_temp, self._target_temp) if not self._active or not self._enabled: return if not force and time is None: # If the `force` argument is True, we # ignore `min_cycle_duration`. # If the `time` argument is not none, we were invoked for # keep-alive purposes, and `min_cycle_duration` is irrelevant. if self.min_cycle_duration: if self._is_device_active: current_state = STATE_ON else: current_state = STATE_OFF long_enough = condition.state( self.hass, self.heater_entity_id, current_state, self.min_cycle_duration) if not long_enough: return too_cold = \ self._target_temp - self._cur_temp >= self._cold_tolerance too_hot = \ self._cur_temp - self._target_temp >= self._hot_tolerance if self._is_device_active: if (self.ac_mode and too_cold) or \ (not self.ac_mode and too_hot): _LOGGER.info("Turning off heater %s", self.heater_entity_id) await self._async_heater_turn_off() elif time is not None: # The time argument is passed only in keep-alive case await self._async_heater_turn_on() else: if (self.ac_mode and too_hot) or \ (not self.ac_mode and too_cold): _LOGGER.info("Turning on heater %s", self.heater_entity_id) await self._async_heater_turn_on() elif time is not None: # The time argument is passed only in keep-alive case await self._async_heater_turn_off()
async def _async_control_heating(self, time=None): """Check if we need to turn heating on or off.""" async with self._temp_lock: if not self._active and None not in (self._cur_temp, self._target_temp): self._active = True _LOGGER.info( "Obtained current and target temperature. " "Generic thermostat active. %s, %s", self._cur_temp, self._target_temp) if not self._active or not self._enabled: return if self.min_cycle_duration: if self._is_device_active: current_state = STATE_ON else: current_state = STATE_OFF long_enough = condition.state(self.hass, self.heater_entity_id, current_state, self.min_cycle_duration) if not long_enough: return too_cold = \ self._target_temp - self._cur_temp >= self._cold_tolerance too_hot = \ self._cur_temp - self._target_temp >= self._hot_tolerance if self._is_device_active: if (self.ac_mode and too_cold) or \ (not self.ac_mode and too_hot): _LOGGER.info("Turning off heater %s", self.heater_entity_id) await self._async_heater_turn_off() elif time is not None: # The time argument is passed only in keep-alive case await self._async_heater_turn_on() else: if (self.ac_mode and too_hot) or \ (not self.ac_mode and too_cold): _LOGGER.info("Turning on heater %s", self.heater_entity_id) await self._async_heater_turn_on() elif time is not None: # The time argument is passed only in keep-alive case await self._async_heater_turn_off()
def _control_heating(self): """Check if we need to turn heating on or off.""" if not self._active and None not in (self._cur_temp, self._target_temp): self._active = True _LOGGER.info('Obtained current and target temperature. ' 'Generic thermostat active.') if not self._active: return if self.min_cycle_duration: if self._is_device_active: current_state = STATE_ON else: current_state = STATE_OFF long_enough = condition.state(self.hass, self.heater_entity_id, current_state, self.min_cycle_duration) if not long_enough: return if self.ac_mode: too_hot = self._cur_temp - self._target_temp > self._tolerance is_cooling = self._is_device_active if too_hot and not is_cooling: _LOGGER.info('Turning on AC %s', self.heater_entity_id) switch.turn_on(self.hass, self.heater_entity_id) elif not too_hot and is_cooling: _LOGGER.info('Turning off AC %s', self.heater_entity_id) switch.turn_off(self.hass, self.heater_entity_id) else: too_cold = self._target_temp - self._cur_temp > self._tolerance is_heating = self._is_device_active if too_cold and not is_heating: _LOGGER.info('Turning on heater %s', self.heater_entity_id) switch.turn_on(self.hass, self.heater_entity_id) elif not too_cold and is_heating: _LOGGER.info('Turning off heater %s', self.heater_entity_id) switch.turn_off(self.hass, self.heater_entity_id)
def _control_heating(self): """Check if we need to turn heating on or off.""" if not self._active and None not in (self._cur_temp, self._target_temp): self._active = True _LOGGER.info('Obtained current and target temperature. ' 'Generic thermostat active.') if not self._active: return if self.min_cycle_duration: if self._is_device_active: current_state = STATE_ON else: current_state = STATE_OFF long_enough = condition.state(self.hass, self.heater_entity_id, current_state, self.min_cycle_duration) if not long_enough: return if self.ac_mode: too_hot = self._cur_temp - self._target_temp > TOL_TEMP is_cooling = self._is_device_active if too_hot and not is_cooling: _LOGGER.info('Turning on AC %s', self.heater_entity_id) switch.turn_on(self.hass, self.heater_entity_id) elif not too_hot and is_cooling: _LOGGER.info('Turning off AC %s', self.heater_entity_id) switch.turn_off(self.hass, self.heater_entity_id) else: too_cold = self._target_temp - self._cur_temp > TOL_TEMP is_heating = self._is_device_active if too_cold and not is_heating: _LOGGER.info('Turning on heater %s', self.heater_entity_id) switch.turn_on(self.hass, self.heater_entity_id) elif not too_cold and is_heating: _LOGGER.info('Turning off heater %s', self.heater_entity_id) switch.turn_off(self.hass, self.heater_entity_id)
async def _async_control_heating(self, time=None, force=False): """Check if we need to turn heating on or off.""" async with self._temp_lock: if not self._active and None not in (self._cur_temp, self._target_temp): self._active = True _LOGGER.info( "Obtained current and target temperature. " "Generic Dual-mode thermostat active. %s, %s", self._cur_temp, self._target_temp, ) if not self._active or self._hvac_mode == HVAC_MODE_OFF: return # This variable is used for the long_enough condition and for the LOG Messages if not force and time is None: # If the `force` argument is True, we # ignore `min_cycle_duration`. # If the `time` argument is not none, we were invoked for # keep-alive purposes, and `min_cycle_duration` is irrelevant. if self.min_cycle_duration: if self._hvac_mode == HVAC_MODE_COOL: active_entity = self.cooler_entity_id if self._hvac_mode == HVAC_MODE_HEAT: active_entity = self.heater_entity_id if self._hvac_mode == HVAC_MODE_FAN_ONLY: active_entity = self.fan_entity_id if self._hvac_mode == HVAC_MODE_DRY: active_entity = self.dryer_entity_id if self._is_device_active: current_state = STATE_ON else: current_state = HVAC_MODE_OFF long_enough = condition.state( self.hass, active_entity, current_state, self.min_cycle_duration, ) if not long_enough: return too_cold = self._target_temp >= self._cur_temp + self._cold_tolerance too_hot = self._cur_temp >= self._target_temp + self._hot_tolerance if self._is_device_active: # when to turn off if too_cold and self._hvac_mode == HVAC_MODE_COOL: _LOGGER.info("Turning off cooler %s", self.cooler_entity_id) await self._async_cooler_turn_off() elif too_hot and self._hvac_mode == HVAC_MODE_HEAT: _LOGGER.info("Turning off heater %s", self.heater_entity_id) await self._async_heater_turn_off() elif self._hvac_mode == HVAC_MODE_FAN_ONLY: if too_cold and self.fan_behavior == FAN_MODE_COOL: _LOGGER.info("Turning off fan %s", self.fan_entity_id) await self._async_fan_turn_off() elif too_hot and self.fan_behavior == FAN_MODE_HEAT: _LOGGER.info("Turning off fan %s", self.fan_entity_id) await self._async_fan_turn_off() elif self._hvac_mode == HVAC_MODE_DRY: if too_cold and self.dryer_behavior == DRYER_MODE_COOL: _LOGGER.info("Turning off dehumidifier %s", self.dryer_entity_id) await self._async_dryer_turn_off() elif too_hot and self.dryer_behavior == DRYER_MODE_HEAT: _LOGGER.info("Turning off dehumidifier %s", self.dryer_entity_id) await self._async_dryer_turn_off() elif time is not None: # The time argument is passed only in keep-alive case _LOGGER.info("Keep-alive - Turning on heater %s", active_entity) if self._hvac_mode == HVAC_MODE_COOL: await self._async_cooler_turn_on() elif self._hvac_mode == HVAC_MODE_HEAT: await self._async_heater_turn_on() elif self._hvac_mode == HVAC_MODE_FAN_ONLY: await self._async_fan_turn_on() elif self._hvac_mode == HVAC_MODE_DRY: await self._async_dryer_turn_on() else: # when to turn on if too_hot and self._hvac_mode == HVAC_MODE_COOL: _LOGGER.info("Turning on cooler %s", self.cooler_entity_id) await self._async_cooler_turn_on() elif too_cold and self._hvac_mode == HVAC_MODE_HEAT: _LOGGER.info("Turning on heater %s", self.heater_entity_id) await self._async_heater_turn_on() elif self._hvac_mode == HVAC_MODE_FAN_ONLY: if too_hot and self.fan_behavior == FAN_MODE_COOL: _LOGGER.info("Turning on fan %s", self.fan_entity_id) await self._async_fan_turn_on() elif too_cold and self.fan_behavior == FAN_MODE_HEAT: _LOGGER.info("Turning on fan %s", self.fan_entity_id) await self._async_fan_turn_on() elif self._hvac_mode == HVAC_MODE_DRY: if too_hot and self.dryer_behavior == DRYER_MODE_COOL: _LOGGER.info("Turning on dehumidifier %s", self.dryer_entity_id) await self._async_dryer_turn_on() elif too_cold and self.dryer_behavior == DRYER_MODE_HEAT: _LOGGER.info("Turning on dehumidifier %s", self.dryer_entity_id) await self._async_dryer_turn_on() elif time is not None: # The time argument is passed only in keep-alive case _LOGGER.info("Keep-alive - Turning off heater %s", active_entity) if self._hvac_mode == HVAC_MODE_COOL: await self._async_cooler_turn_off() elif self._hvac_mode == HVAC_MODE_HEAT: await self._async_heater_turn_off() elif self._hvac_mode == HVAC_MODE_FAN_ONLY: await self._async_fan_turn_off() elif self._hvac_mode == HVAC_MODE_DRY: await self._async_dryer_turn_off() if self.fan_behavior == FAN_MODE_NEUTRAL and self._hvac_mode == HVAC_MODE_FAN_ONLY: await self._async_fan_turn_on() if self.dryer_behavior == DRYER_MODE_NEUTRAL and self._hvac_mode == HVAC_MODE_DRY: await self._async_dryer_turn_on()
def _async_control_heating(self): """Check if we need to turn heating on or off.""" if not self._active and None not in (self._cur_temp, self._target_temp): self._active = True _LOGGER.info('Obtained current and target temperature. ' 'Generic thermostat active.') if not self._active: return if not self._enabled: return if self.min_cycle_duration: if self._is_device_active: current_state = STATE_ON else: current_state = STATE_OFF long_enough = condition.state(self.hass, self.heater_entity_id, current_state, self.min_cycle_duration) if not long_enough: return if self.ac_mode: is_cooling = self._is_device_active #if is_cooling: #too_cold = self._target_temp - self._cur_temp >= \ #self._cold_tolerance #if too_cold: #_LOGGER.info('Turning off AC %s', self.heater_entity_id) #self._heater_turn_off() #else: #too_hot = self._cur_temp - self._target_temp >= \ #self._hot_tolerance #if too_hot: #_LOGGER.info('Turning on AC %s', self.heater_entity_id) #self._heater_turn_on() else: is_heating = self._is_device_active if self._target_temp < self._min_heating_temp: # or operation mode is off if is_heating: _LOGGER.info('Turning of heatpump {0}'.format( self.heater_entity_id)) self._heatpump_set_mode('OFF') else: return elif is_heating: too_hot = self._cur_temp - self._target_temp >= \ self._hot_tolerance if too_hot: _LOGGER.info('Turning heatpump %s to FAN', self.heater_entity_id) self._heatpump_set_mode('FAN') else: too_cold = self._target_temp - self._cur_temp >= \ self._cold_tolerance if too_cold: _LOGGER.info('Turning heatpump %s to HEAT', self.heater_entity_id) self._heatpump_set_mode('HEAT')
async def _async_control_heating(self, time=None, force=False): """Check if we need to turn heating on or off.""" async with self._temp_lock: if not self._active and None not in (self._cur_temp, self._target_temp): self._active = True _LOGGER.info( "Obtained current and target temperature. " "Generic Dual-mode thermostat active. %s, %s", self._cur_temp, self._target_temp, ) if not self._active or self._hvac_mode == HVAC_MODE_OFF: return if not force and time is None: # If the `force` argument is True, we # ignore `min_cycle_duration`. # If the `time` argument is not none, we were invoked for # keep-alive purposes, and `min_cycle_duration` is irrelevant. if self.min_cycle_duration: entity = (self.cooler_entity_id if self._hvac_mode == HVAC_MODE_COOL else self.heater_entity_id) if self._is_device_active: current_state = STATE_ON else: current_state = HVAC_MODE_OFF long_enough = condition.state( self.hass, entity, current_state, self.min_cycle_duration, ) if not long_enough: return # self._hvac_mode can only be HVAC_MODE_HEAT or HVAC_MODE_COOL at this point if self.sensor_mode == SENSOR_MODE_SMART: if self._hvac_mode == HVAC_MODE_HEAT: too_cold = (self._fs_heat_min_temp >= self._cur_floor_temp + self._cold_tolerance or (self._target_temp >= self._cur_temp + self._cold_tolerance and self._fs_heat_max_temp >= self._cur_floor_temp + self._cold_tolerance)) too_hot = (self._cur_floor_temp >= self._fs_heat_max_temp + self._hot_tolerance or (self._cur_floor_temp >= self._fs_heat_min_temp + self._hot_tolerance and self._cur_temp >= self._target_temp + self._hot_tolerance)) elif self._hvac_mode == HVAC_MODE_COOL: too_cold = (self._cur_floor_temp + self._cold_tolerance <= self._fs_cool_min_temp or (self._fs_cool_max_temp >= self._cur_floor_temp + self._cold_tolerance and self._target_temp >= self._cur_temp + self._cold_tolerance)) too_hot = (self._cur_floor_temp >= self._fs_cool_max_temp + self._hot_tolerance or (self._cur_temp >= self._target_temp + self._hot_tolerance and self._cur_floor_temp >= self._fs_cool_min_temp + self._hot_tolerance)) else: too_cold = False too_hot = False _LOGGER.error("Smart mode, HVAC_MODE nor heat or cool") elif self.sensor_mode == SENSOR_MODE_FLOOR: if self._hvac_mode == HVAC_MODE_HEAT: too_cold = (self._fs_heat_min_temp >= self._cur_floor_temp + self._cold_tolerance or (self._target_temp >= self._cur_floor_temp + self._cold_tolerance and self._fs_heat_max_temp >= self._cur_floor_temp + self._cold_tolerance)) too_hot = (self._cur_floor_temp >= self._fs_heat_max_temp + self._hot_tolerance or (self._cur_floor_temp >= self._fs_heat_min_temp + self._hot_tolerance and self._cur_floor_temp >= self._target_temp + self._hot_tolerance)) elif self._hvac_mode == HVAC_MODE_COOL: too_cold = (self._fs_cool_min_temp >= self._cur_floor_temp + self._cold_tolerance or (self._fs_cool_max_temp >= self._cur_floor_temp + self._cold_tolerance and self._target_temp >= self._cur_floor_temp + self._cold_tolerance)) too_hot = (self._cur_floor_temp >= self._fs_cool_max_temp + self._hot_tolerance or (self._cur_floor_temp >= self._target_temp + self._hot_tolerance and self._target_temp > self._fs_cool_min_temp + self._hot_tolerance)) else: too_cold = False too_hot = False _LOGGER.error("Floor mode, HVAC_MODE nor heat or cool") else: # self.sensor_mode == SENSOR_MODE_AMBIENT: too_cold = self._target_temp >= self._cur_temp + self._cold_tolerance too_hot = self._cur_temp >= self._target_temp + self._hot_tolerance if self._is_device_active: # when to turn off if too_cold and self._hvac_mode == HVAC_MODE_COOL: _LOGGER.info("Turning off cooler %s", self.cooler_entity_id) await self._async_cooler_turn_off() elif too_hot and self._hvac_mode == HVAC_MODE_HEAT: _LOGGER.info("Turning off heater %s", self.heater_entity_id) await self._async_heater_turn_off() elif time is not None: # The time argument is passed only in keep-alive case _LOGGER.info( "Keep-alive - Turning on heater heater %s", self.heater_entity_id if self._hvac_mode == HVAC_MODE_HEAT else self.cooler_entity_id, ) if self._hvac_mode == HVAC_MODE_COOL: await self._async_cooler_turn_on() elif self._hvac_mode == HVAC_MODE_HEAT: await self._async_heater_turn_on() else: # when to turn on if too_hot and self._hvac_mode == HVAC_MODE_COOL: _LOGGER.info("Turning on cooler %s", self.cooler_entity_id) await self._async_cooler_turn_on() elif too_cold and self._hvac_mode == HVAC_MODE_HEAT: _LOGGER.info("Turning on heater %s", self.heater_entity_id) await self._async_heater_turn_on() elif time is not None: # The time argument is passed only in keep-alive case _LOGGER.info( "Keep-alive - Turning off heater %s", self.heater_entity_id if self._hvac_mode == HVAC_MODE_HEAT else self.cooler_entity_id, ) if self._hvac_mode == HVAC_MODE_COOL: await self._async_cooler_turn_off() elif self._hvac_mode == HVAC_MODE_HEAT: await self._async_heater_turn_off()
async def _async_control_heating(self, time=None, force=False): """Check if we need to turn heating on or off.""" _LOGGER.debug("Check if we need to turn heating on or off") async with self._temp_lock: if not self._active and None not in (self._cur_temp, self._target_temp): self._active = True _LOGGER.info("Obtained current and target temperature. " "Generic thermostat active. %s, %s", self._cur_temp, self._target_temp) if not self._active or not self._enabled: return next_state = self._current_operation is_heating = self._is_device_active if is_heating: too_hot = self._cur_temp - self._target_temp >= \ self._hot_tolerance if too_hot: next_state = STATE_STANDBY else: _LOGGER.debug("Evaluate regulation mode for heater %s", self.heater_entity_id) regulation_mode = self._cur_temp >= \ self._target_temp - self._regulation_delta if regulation_mode: next_state = STATE_REGULATION else: next_state = STATE_HEAT else: too_cold = self._target_temp - self._cur_temp >= \ self._cold_tolerance if too_cold: next_state = STATE_HEAT else: next_state = STATE_STANDBY _LOGGER.debug("Next state for heater %s : %s", self.heater_entity_id, next_state) if not force and time is None: # If the `force` argument is True, we # ignore `min_cycle_duration`. # If the `time` argument is not none, we were invoked for # keep-alive purposes, and `min_cycle_duration` is irrelevant. if self.min_cycle_duration: if self._is_device_active: current_state = STATE_ON else: current_state = STATE_OFF if next_state == STATE_STANDBY: next_current_state = STATE_OFF else: next_current_state = STATE_ON if current_state != next_current_state: _LOGGER.debug("State : %s since %s", self.hass.states.get(self._heat_entity_id).state, self.hass.states.get(self._heat_entity_id).last_changed) long_enough = condition.state( self.hass, self._heat_entity_id, current_state, self.min_cycle_duration) if not long_enough: _LOGGER.debug("Min cycle duration not reach for heater %s", self._heat_entity_id) return await self._async_set_heating_mode(next_state, time)
async def _async_operate(self, time=None, force=False): """Check if we need to turn humidifying on or off.""" async with self._humidity_lock: if not self._active and None not in ( self._cur_humidity, self._target_humidity, ): self._active = True force = True _LOGGER.info( "Obtained current and target humidity. " "Generic hygrostat active. %s, %s", self._cur_humidity, self._target_humidity, ) if not self._active or not self._state: return if not force and time is None: # If the `force` argument is True, we # ignore `min_cycle_duration`. # If the `time` argument is not none, we were invoked for # keep-alive purposes, and `min_cycle_duration` is irrelevant. if self._min_cycle_duration: if self._is_device_active: current_state = STATE_ON else: current_state = STATE_OFF long_enough = condition.state( self.hass, self._switch_entity_id, current_state, self._min_cycle_duration, ) if not long_enough: return if force: # Ignore the tolerance when switched on manually dry_tolerance = 0 wet_tolerance = 0 else: dry_tolerance = self._dry_tolerance wet_tolerance = self._wet_tolerance too_dry = self._target_humidity - self._cur_humidity >= dry_tolerance too_wet = self._cur_humidity - self._target_humidity >= wet_tolerance if self._is_device_active: if ( self._device_class == HumidifierDeviceClass.HUMIDIFIER and too_wet ) or ( self._device_class == HumidifierDeviceClass.DEHUMIDIFIER and too_dry ): _LOGGER.info("Turning off humidifier %s", self._switch_entity_id) await self._async_device_turn_off() elif time is not None: # The time argument is passed only in keep-alive case await self._async_device_turn_on() else: if ( self._device_class == HumidifierDeviceClass.HUMIDIFIER and too_dry ) or ( self._device_class == HumidifierDeviceClass.DEHUMIDIFIER and too_wet ): _LOGGER.info("Turning on humidifier %s", self._switch_entity_id) await self._async_device_turn_on() elif time is not None: # The time argument is passed only in keep-alive case await self._async_device_turn_off()
async def _async_control_heating(self, time=None, force=False): """Check if we need to turn heating on or off.""" _LOGGER.debug("Check if we need to turn heating on or off") async with self._temp_lock: if not self._active and None not in (self._cur_temp, self._target_temp): self._active = True _LOGGER.info( "Obtained current and target temperature. " "Generic thermostat active. %s, %s", self._cur_temp, self._target_temp, ) if not self._active or self._hvac_mode == HVAC_MODE_OFF: return #CCL next_state = self._hvac_mode #End CCL if not force and time is None: # If the `force` argument is True, we # ignore `min_cycle_duration`. # If the `time` argument is not none, we were invoked for # keep-alive purposes, and `min_cycle_duration` is irrelevant. if self.min_cycle_duration: if self._is_device_active: current_state = STATE_ON else: current_state = HVAC_MODE_OFF #CCL if next_state == HVAC_MODE_IDLE: next_current_state = HVAC_MODE_OFF else: next_current_state = STATE_ON if current_state != next_current_state: _LOGGER.debug( "State : %s since %s", self.hass.states.get(self._heat_entity_id).state, self.hass.states.get( self._heat_entity_id).last_changed) #End CCL long_enough = condition.state( self.hass, self.heater_entity_id, current_state, self.min_cycle_duration, ) if not long_enough: _LOGGER.debug( "Min cycle duration not reach for heater %s", self._heat_entity_id) return too_cold = self._target_temp - self._cur_temp >= self._cold_tolerance too_hot = self._cur_temp - self._target_temp >= self._hot_tolerance # CCL : Replace fallowing by ... #if self._is_device_active: # if (self.ac_mode and too_cold) or (not self.ac_mode and too_hot): # _LOGGER.info("Turning off heater %s", self.heater_entity_id) # await self._async_heater_turn_off() # elif time is not None: # # The time argument is passed only in keep-alive case # await self._async_heater_turn_on() #else: # if (self.ac_mode and too_hot) or (not self.ac_mode and too_cold): # _LOGGER.info("Turning on heater %s", self.heater_entity_id) # await self._async_heater_turn_on() # elif time is not None: # # The time argument is passed only in keep-alive case # await self._async_heater_turn_off() if self._is_device_active: if too_hot: next_state = HVAC_MODE_IDLE else: _LOGGER.debug("Evaluate regulation mode for heater %s", self.heater_entity_id) regulation_mode = self._cur_temp >= \ self._target_temp - self._regulation_delta if regulation_mode: next_state = HVAC_MODE_REGULATION else: next_state = HVAC_MODE_COOL else: if too_cold: next_state = HVAC_MODE_COOL else: next_state = HVAC_MODE_IDLE _LOGGER.debug( "Next state for heater %s : %s (force: %s, time: %s)", self.heater_entity_id, next_state, force, time) await self._async_set_heating_mode(next_state, time)
def test_is_state(hass: HomeAssistant, variables: TemplateVarsType) -> bool: """Test if an entity is a certain state.""" return condition.state(hass, config[CONF_ENTITY_ID], config[CONF_OPTION], config.get(CONF_FOR))
def test_is_on(hass: HomeAssistant, variables: TemplateVarsType) -> bool: """Test if an entity is on.""" return condition.state(hass, config[ATTR_ENTITY_ID], STATE_ON)
async def _async_control_heating(self, time=None, force=False): """Check if we need to turn heating on or off.""" async with self._temp_lock: if not self._active and None not in (self._cur_temp, self._target_temp): self._active = True _LOGGER.info( "Obtained current and target temperature. " "Dual smart thermostat active. %s, %s", self._cur_temp, self._target_temp, ) if not self._active or self._hvac_mode == HVAC_MODE_OFF: return if not force and time is None: # If the `force` argument is True, we # ignore `min_cycle_duration`. # If the `time` argument is not none, we were invoked for # keep-alive purposes, and `min_cycle_duration` is irrelevant. if self.min_cycle_duration: if self._is_device_active: current_state = STATE_ON else: current_state = HVAC_MODE_OFF long_enough = condition.state( self.hass, self.heater_entity_id, current_state, self.min_cycle_duration, ) if not long_enough: return too_cold = self._target_temp >= self._cur_temp + self._cold_tolerance too_hot = self._cur_temp >= self._target_temp + self._hot_tolerance if self._is_device_active: if ( (self.ac_mode and too_cold) or (not self.ac_mode and too_hot) or (not self.ac_mode and self._is_floor_hot) or self._is_opening_open ): _LOGGER.info("Turning off heater %s", self.heater_entity_id) await self._async_heater_turn_off() elif ( time is not None and not self._is_opening_open and not self._is_floor_hot ): # The time argument is passed only in keep-alive case _LOGGER.info( "Keep-alive - Turning on heater (from active) %s", self.heater_entity_id, ) await self._async_heater_turn_on() else: if (self.ac_mode and too_hot and not self._is_opening_open) or ( not self.ac_mode and too_cold and not self._is_opening_open and not self._is_floor_hot ): _LOGGER.info( "Turning on heater (from inactive) %s", self.heater_entity_id ) await self._async_heater_turn_on() elif time is not None or self._is_opening_open or self._is_floor_hot: # The time argument is passed only in keep-alive case _LOGGER.info( "Keep-alive - Turning off heater %s", self.heater_entity_id ) await self._async_heater_turn_off()
def test_is_state(hass: HomeAssistant, variables: TemplateVarsType) -> bool: """Test if an entity is a certain state.""" return condition.state(hass, config[ATTR_ENTITY_ID], state)
async def _async_control_heating(self, force=False): _LOGGER.debug("running _async_control_heating") """Check if we need to turn heating on or off.""" async with self._temp_lock: if not self._active and None not in (self._cur_temp, self._target_temp_high, self._target_temp_low): self._active = True _LOGGER.info( "Obtained current and target temperature. " "Generic thermostat active. %s, %s, %s", self._cur_temp, self._target_temp_low, self._target_temp_high) if not self._active: _LOGGER.debug("not active") return if self._hvac_mode == HVAC_MODE_OFF: _LOGGER.debug("Mode is off") return if not force: _LOGGER.debug("force = false") # If the `force` argument is True, we # ignore `min_cycle_duration`. if self.min_cycle_duration: _LOGGER.debug("min_cycle_duration = false") entity = self.cooler_entity_id if self._hvac_mode == HVAC_MODE_COOL else self.heater_entity_id if self._is_device_active: current_state = STATE_ON else: current_state = HVAC_MODE_OFF long_enough = condition.state( self.hass, entity, current_state, self.min_cycle_duration, ) if not long_enough: _LOGGER.debug("not long enough") return too_cold = self._target_temp_low - self._cur_temp >= self._cold_tolerance too_hot = self._cur_temp - self._target_temp_high >= self._hot_tolerance cool_enough = self._target_temp_high - self._cur_temp >= self._hot_tolerance warm_enough = self._cur_temp - self._target_temp_low >= self._cold_tolerance _LOGGER.debug( "States: too_cold=%s too_hot=%s cool_enough=%s warm_enough=%s", too_cold, too_hot, cool_enough, warm_enough) _LOGGER.debug("Mode: %s, %s", self._hvac_mode, self._is_device_active) if self._is_device_active: # when to turn off if cool_enough and (self._hvac_mode == HVAC_MODE_COOL or self._hvac_mode == HVAC_MODE_HEAT_COOL): _LOGGER.info("Turning off cooler %s", self.cooler_entity_id) await self._async_cooler_turn_off() elif warm_enough and (self._hvac_mode == HVAC_MODE_HEAT or self._hvac_mode == HVAC_MODE_HEAT_COOL): _LOGGER.info("Turning off heater %s", self.heater_entity_id) await self._async_heater_turn_off() else: # when to turn on if too_hot and (self._hvac_mode == HVAC_MODE_COOL or self._hvac_mode == HVAC_MODE_HEAT_COOL): _LOGGER.info("Turning on cooler %s", self.cooler_entity_id) await self._async_cooler_turn_on() elif too_cold and (self._hvac_mode == HVAC_MODE_HEAT or self._hvac_mode == HVAC_MODE_HEAT_COOL): _LOGGER.info("Turning on heater %s", self.heater_entity_id) await self._async_heater_turn_on()