diff --git a/custom_components/versatile_thermostat/climate.py b/custom_components/versatile_thermostat/climate.py index 6897618..f9a1904 100644 --- a/custom_components/versatile_thermostat/climate.py +++ b/custom_components/versatile_thermostat/climate.py @@ -20,6 +20,7 @@ from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_component import EntityComponent +import homeassistant.helpers.config_validation as cv from homeassistant.helpers.event import ( async_track_state_change_event, @@ -114,6 +115,7 @@ from .const import ( PROPORTIONAL_FUNCTION_TPI, SERVICE_SET_PRESENCE, SERVICE_SET_PRESET_TEMPERATURE, + SERVICE_SET_SECURITY, PRESET_AWAY_SUFFIX, CONF_SECURITY_DELAY_MIN, CONF_SECURITY_MIN_ON_PERCENT, @@ -178,6 +180,16 @@ async def async_setup_entry( "service_set_preset_temperature", ) + platform.async_register_entity_service( + SERVICE_SET_SECURITY, + { + vol.Optional("delay_min"): cv.positive_int, + vol.Optional("min_on_percent"): vol.Coerce(float), + vol.Optional("default_on_percent"): vol.Coerce(float), + }, + "service_set_security", + ) + class VersatileThermostat(ClimateEntity, RestoreEntity): """Representation of a Versatile Thermostat device.""" @@ -751,9 +763,13 @@ class VersatileThermostat(ClimateEntity, RestoreEntity): ): self._attr_preset_mode = old_state.attributes.get(ATTR_PRESET_MODE) self.save_preset_mode() + else: + self._attr_preset_mode = PRESET_NONE if not self._hvac_mode and old_state.state: self._hvac_mode = old_state.state + else: + self._hvac_mode = HVACMode.OFF else: # No previous state, try and restore defaults @@ -1697,7 +1713,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity): now - self._last_ext_temperature_mesure ).total_seconds() / 60.0 - mode_cond = self._hvac_mode != HVACMode.OFF + mode_cond = self._is_over_climate or self._hvac_mode != HVACMode.OFF temp_cond: bool = ( delta_temp > self._security_delay_min @@ -1711,7 +1727,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity): not self._is_over_climate and self._prop_algorithm is not None and self._prop_algorithm.calculated_on_percent - > self._security_min_on_percent + >= self._security_min_on_percent ) ret = False @@ -2099,6 +2115,36 @@ class VersatileThermostat(ClimateEntity, RestoreEntity): await self._async_set_preset_mode_internal(preset, force=True) await self._async_control_heating(force=True) + async def service_set_security(self, delay_min, min_on_percent, default_on_percent): + """Called by a service call: + service: versatile_thermostat.set_security + data: + delay_min: 15 + min_on_percent: 0.5 + default_on_percent: 0.2 + target: + entity_id: climate.thermostat_2 + """ + _LOGGER.info( + "%s - Calling service_set_security, delay_min: %s, min_on_percent: %s, default_on_percent: %s", + self, + delay_min, + min_on_percent, + default_on_percent, + ) + if delay_min: + self._security_delay_min = delay_min + if min_on_percent: + self._security_min_on_percent = min_on_percent + if default_on_percent: + self._security_default_on_percent = default_on_percent + + if self._prop_algorithm and self._security_state: + self._prop_algorithm.set_security(self._security_default_on_percent) + + await self._async_control_heating() + self.update_custom_attributes() + def send_event(self, event_type: EventType, data: dict): """Send an event""" _LOGGER.info("%s - Sending event %s with data: %s", self, event_type, data) diff --git a/custom_components/versatile_thermostat/const.py b/custom_components/versatile_thermostat/const.py index e75f4de..3802521 100644 --- a/custom_components/versatile_thermostat/const.py +++ b/custom_components/versatile_thermostat/const.py @@ -130,12 +130,15 @@ SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE SERVICE_SET_PRESENCE = "set_presence" SERVICE_SET_PRESET_TEMPERATURE = "set_preset_temperature" +SERVICE_SET_SECURITY = "set_security" DEFAULT_SECURITY_MIN_ON_PERCENT = 0.5 DEFAULT_SECURITY_DEFAULT_ON_PERCENT = 0.1 class EventType(Enum): + """The event type that can be sent""" + SECURITY_EVENT: str = "versatile_thermostat_security_event" POWER_EVENT: str = "versatile_thermostat_power_event" TEMPERATURE_EVENT: str = "versatile_thermostat_temperature_event" diff --git a/custom_components/versatile_thermostat/services.yaml b/custom_components/versatile_thermostat/services.yaml index d64d9d9..792df12 100644 --- a/custom_components/versatile_thermostat/services.yaml +++ b/custom_components/versatile_thermostat/services.yaml @@ -1,69 +1,117 @@ set_presence: - name: Set presence - description: Force the presence mode in thermostat - target: - entity: - integration: versatile_thermostat - fields: - presence: - name: Presence - description: Presence setting - required: true - advanced: false - example: "on" - default: "on" - selector: - select: - options: - - "on" - - "off" - - "home" - - "not_home" + name: Set presence + description: Force the presence mode in thermostat + target: + entity: + integration: versatile_thermostat + fields: + presence: + name: Presence + description: Presence setting + required: true + advanced: false + example: "on" + default: "on" + selector: + select: + options: + - "on" + - "off" + - "home" + - "not_home" set_preset_temperature: - name: Set temperature preset - description: Change the target temperature of a preset - target: - entity: - integration: versatile_thermostat - fields: - preset: - name: Preset - description: Preset name - required: true - advanced: false - example: "comfort" - selector: - select: - options: - - "eco" - - "comfort" - - "boost" - temperature: - name: Temperature when present - description: Target temperature for the preset when present - required: false - advanced: false - example: "19.5" - default: "17" - selector: - number: - min: 7 - max: 35 - step: 0.1 - unit_of_measurement: ° - mode: slider - temperature_away: - name: Temperature when not present - description: Target temperature for the preset when not present - required: false - advanced: false - example: "17" - default: "15" - selector: - number: - min: 7 - max: 35 - step: 0.1 - unit_of_measurement: ° - mode: slider \ No newline at end of file + name: Set temperature preset + description: Change the target temperature of a preset + target: + entity: + integration: versatile_thermostat + fields: + preset: + name: Preset + description: Preset name + required: true + advanced: false + example: "comfort" + selector: + select: + options: + - "eco" + - "comfort" + - "boost" + temperature: + name: Temperature when present + description: Target temperature for the preset when present + required: false + advanced: false + example: "19.5" + default: "17" + selector: + number: + min: 7 + max: 35 + step: 0.1 + unit_of_measurement: ° + mode: slider + temperature_away: + name: Temperature when not present + description: Target temperature for the preset when not present + required: false + advanced: false + example: "17" + default: "15" + selector: + number: + min: 7 + max: 35 + step: 0.1 + unit_of_measurement: ° + mode: slider + +set_security: + name: Set security + description: Change the security parameters + target: + entity: + integration: versatile_thermostat + fields: + delay_min: + name: Delay in minutes + description: Maximum allowed delay in minutes between two temperature mesures + required: false + advanced: false + example: "30" + selector: + number: + min: 0 + max: 9999 + unit_of_measurement: "min" + mode: box + min_on_percent: + name: Minimal on_percent + description: Minimal heating percent value for security preset activation + required: false + advanced: false + example: "0.5" + default: "0.5" + selector: + number: + min: 0 + max: 1 + step: 0.05 + unit_of_measurement: "%" + mode: slider + default_on_percent: + name: on_percent used in security mode + description: The default heating percent value in security preset + required: false + advanced: false + example: "0.1" + default: "0.1" + selector: + number: + min: 0 + max: 1 + step: 0.05 + unit_of_measurement: "%" + mode: slider