Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f970c18eaf | |||
| af51ef62e0 | |||
| b38fbd9d78 | |||
| 6e8e72e343 | |||
| 2bebe3e210 | |||
| aa3b87762d | |||
| f4cabbf2c0 | |||
| 24b59e545b |
+175
-175
@@ -1,196 +1,196 @@
|
|||||||
default_config:
|
default_config:
|
||||||
|
|
||||||
logger:
|
logger:
|
||||||
default: info
|
default: info
|
||||||
logs:
|
logs:
|
||||||
custom_components.versatile_thermostat: info
|
custom_components.versatile_thermostat: info
|
||||||
custom_components.versatile_thermostat.underlyings: debug
|
custom_components.versatile_thermostat.underlyings: debug
|
||||||
custom_components.versatile_thermostat.climate: debug
|
custom_components.versatile_thermostat.climate: debug
|
||||||
|
|
||||||
# If you need to debug uncommment the line below (doc: https://www.home-assistant.io/integrations/debugpy/)
|
# If you need to debug uncommment the line below (doc: https://www.home-assistant.io/integrations/debugpy/)
|
||||||
debugpy:
|
debugpy:
|
||||||
start: true
|
start: true
|
||||||
wait: false
|
wait: false
|
||||||
port: 5678
|
port: 5678
|
||||||
|
|
||||||
input_number:
|
input_number:
|
||||||
fake_temperature_sensor1:
|
fake_temperature_sensor1:
|
||||||
name: Temperature
|
name: Temperature
|
||||||
min: 0
|
min: 0
|
||||||
max: 35
|
max: 35
|
||||||
step: .1
|
step: .1
|
||||||
icon: mdi:thermometer
|
icon: mdi:thermometer
|
||||||
unit_of_measurement: °C
|
unit_of_measurement: °C
|
||||||
mode: box
|
mode: box
|
||||||
fake_external_temperature_sensor1:
|
fake_external_temperature_sensor1:
|
||||||
name: Ext Temperature
|
name: Ext Temperature
|
||||||
min: -10
|
min: -10
|
||||||
max: 35
|
max: 35
|
||||||
step: .1
|
step: .1
|
||||||
icon: mdi:home-thermometer
|
icon: mdi:home-thermometer
|
||||||
unit_of_measurement: °C
|
unit_of_measurement: °C
|
||||||
mode: box
|
mode: box
|
||||||
fake_current_power:
|
fake_current_power:
|
||||||
name: Current power
|
name: Current power
|
||||||
min: 0
|
min: 0
|
||||||
max: 1000
|
max: 1000
|
||||||
step: 10
|
step: 10
|
||||||
icon: mdi:flash
|
icon: mdi:flash
|
||||||
unit_of_measurement: kW
|
unit_of_measurement: kW
|
||||||
fake_current_power_max:
|
fake_current_power_max:
|
||||||
name: Current power max threshold
|
name: Current power max threshold
|
||||||
min: 0
|
min: 0
|
||||||
max: 1000
|
max: 1000
|
||||||
step: 10
|
step: 10
|
||||||
icon: mdi:flash
|
icon: mdi:flash
|
||||||
unit_of_measurement: kW
|
unit_of_measurement: kW
|
||||||
|
|
||||||
input_boolean:
|
input_boolean:
|
||||||
# input_boolean to simulate the windows entity. Only for development environment.
|
# input_boolean to simulate the windows entity. Only for development environment.
|
||||||
fake_window_sensor1:
|
fake_window_sensor1:
|
||||||
name: Window 1
|
name: Window 1
|
||||||
icon: mdi:window-closed-variant
|
icon: mdi:window-closed-variant
|
||||||
# input_boolean to simulate the heater entity switch. Only for development environment.
|
# input_boolean to simulate the heater entity switch. Only for development environment.
|
||||||
fake_heater_switch3:
|
fake_heater_switch3:
|
||||||
name: Heater 3
|
name: Heater 3
|
||||||
icon: mdi:radiator
|
icon: mdi:radiator
|
||||||
fake_heater_switch2:
|
fake_heater_switch2:
|
||||||
name: Heater 2
|
name: Heater 2
|
||||||
icon: mdi:radiator
|
icon: mdi:radiator
|
||||||
fake_heater_switch1:
|
fake_heater_switch1:
|
||||||
name: Heater 1
|
name: Heater 1
|
||||||
icon: mdi:radiator
|
icon: mdi:radiator
|
||||||
fake_heater_4switch1:
|
fake_heater_4switch1:
|
||||||
name: Heater (multiswitch1)
|
name: Heater (multiswitch1)
|
||||||
icon: mdi:radiator
|
icon: mdi:radiator
|
||||||
fake_heater_4switch2:
|
fake_heater_4switch2:
|
||||||
name: Heater (multiswitch2)
|
name: Heater (multiswitch2)
|
||||||
icon: mdi:radiator
|
icon: mdi:radiator
|
||||||
fake_heater_4switch3:
|
fake_heater_4switch3:
|
||||||
name: Heater (multiswitch3)
|
name: Heater (multiswitch3)
|
||||||
icon: mdi:radiator
|
icon: mdi:radiator
|
||||||
fake_heater_4switch4:
|
fake_heater_4switch4:
|
||||||
name: Heater (multiswitch4)
|
name: Heater (multiswitch4)
|
||||||
icon: mdi:radiator
|
icon: mdi:radiator
|
||||||
# input_boolean to simulate the motion sensor entity. Only for development environment.
|
# input_boolean to simulate the motion sensor entity. Only for development environment.
|
||||||
fake_motion_sensor1:
|
fake_motion_sensor1:
|
||||||
name: Motion Sensor 1
|
name: Motion Sensor 1
|
||||||
icon: mdi:run
|
icon: mdi:run
|
||||||
# input_boolean to simulate the presence sensor entity. Only for development environment.
|
# input_boolean to simulate the presence sensor entity. Only for development environment.
|
||||||
fake_presence_sensor1:
|
fake_presence_sensor1:
|
||||||
name: Presence Sensor 1
|
name: Presence Sensor 1
|
||||||
icon: mdi:home
|
icon: mdi:home
|
||||||
|
|
||||||
climate:
|
climate:
|
||||||
- platform: generic_thermostat
|
- platform: generic_thermostat
|
||||||
name: Underlying thermostat1
|
name: Underlying thermostat1
|
||||||
heater: input_boolean.fake_heater_switch3
|
heater: input_boolean.fake_heater_switch3
|
||||||
target_sensor: input_number.fake_temperature_sensor1
|
target_sensor: input_number.fake_temperature_sensor1
|
||||||
- platform: generic_thermostat
|
- platform: generic_thermostat
|
||||||
name: Underlying thermostat2
|
name: Underlying thermostat2
|
||||||
heater: input_boolean.fake_heater_switch3
|
heater: input_boolean.fake_heater_switch3
|
||||||
target_sensor: input_number.fake_temperature_sensor1
|
target_sensor: input_number.fake_temperature_sensor1
|
||||||
- platform: generic_thermostat
|
- platform: generic_thermostat
|
||||||
name: Underlying thermostat3
|
name: Underlying thermostat3
|
||||||
heater: input_boolean.fake_heater_switch3
|
heater: input_boolean.fake_heater_switch3
|
||||||
target_sensor: input_number.fake_temperature_sensor1
|
target_sensor: input_number.fake_temperature_sensor1
|
||||||
- platform: generic_thermostat
|
- platform: generic_thermostat
|
||||||
name: Underlying thermostat4
|
name: Underlying thermostat4
|
||||||
heater: input_boolean.fake_heater_switch3
|
heater: input_boolean.fake_heater_switch3
|
||||||
target_sensor: input_number.fake_temperature_sensor1
|
target_sensor: input_number.fake_temperature_sensor1
|
||||||
- platform: generic_thermostat
|
- platform: generic_thermostat
|
||||||
name: Underlying thermostat5
|
name: Underlying thermostat5
|
||||||
heater: input_boolean.fake_heater_switch3
|
heater: input_boolean.fake_heater_switch3
|
||||||
target_sensor: input_number.fake_temperature_sensor1
|
target_sensor: input_number.fake_temperature_sensor1
|
||||||
- platform: generic_thermostat
|
- platform: generic_thermostat
|
||||||
name: Underlying thermostat6
|
name: Underlying thermostat6
|
||||||
heater: input_boolean.fake_heater_switch3
|
heater: input_boolean.fake_heater_switch3
|
||||||
target_sensor: input_number.fake_temperature_sensor1
|
target_sensor: input_number.fake_temperature_sensor1
|
||||||
- platform: generic_thermostat
|
- platform: generic_thermostat
|
||||||
name: Underlying thermostat7
|
name: Underlying thermostat7
|
||||||
heater: input_boolean.fake_heater_switch3
|
heater: input_boolean.fake_heater_switch3
|
||||||
target_sensor: input_number.fake_temperature_sensor1
|
target_sensor: input_number.fake_temperature_sensor1
|
||||||
- platform: generic_thermostat
|
- platform: generic_thermostat
|
||||||
name: Underlying thermostat8
|
name: Underlying thermostat8
|
||||||
heater: input_boolean.fake_heater_switch3
|
heater: input_boolean.fake_heater_switch3
|
||||||
target_sensor: input_number.fake_temperature_sensor1
|
target_sensor: input_number.fake_temperature_sensor1
|
||||||
- platform: generic_thermostat
|
- platform: generic_thermostat
|
||||||
name: Underlying thermostat9
|
name: Underlying thermostat9
|
||||||
heater: input_boolean.fake_heater_switch3
|
heater: input_boolean.fake_heater_switch3
|
||||||
target_sensor: input_number.fake_temperature_sensor1
|
target_sensor: input_number.fake_temperature_sensor1
|
||||||
|
|
||||||
recorder:
|
recorder:
|
||||||
include:
|
include:
|
||||||
domains:
|
domains:
|
||||||
- input_boolean
|
- input_boolean
|
||||||
- input_number
|
- input_number
|
||||||
- switch
|
- switch
|
||||||
- climate
|
- climate
|
||||||
- sensor
|
- sensor
|
||||||
|
|
||||||
template:
|
template:
|
||||||
- binary_sensor:
|
- binary_sensor:
|
||||||
- name: maison_occupee
|
- name: maison_occupee
|
||||||
unique_id: maison_occupee
|
unique_id: maison_occupee
|
||||||
state: "{{is_state('person.jmc', 'home') }}"
|
state: "{{is_state('person.jmc', 'home') }}"
|
||||||
device_class: occupancy
|
device_class: occupancy
|
||||||
- sensor:
|
- sensor:
|
||||||
- name: "Total énergie switch1"
|
- name: "Total énergie switch1"
|
||||||
unique_id: total_energie_switch1
|
unique_id: total_energie_switch1
|
||||||
unit_of_measurement: "kWh"
|
unit_of_measurement: "kWh"
|
||||||
device_class: energy
|
device_class: energy
|
||||||
state_class: total_increasing
|
state_class: total_increasing
|
||||||
state: >
|
state: >
|
||||||
{% set energy = state_attr('climate.thermostat_switch_1', 'total_energy') %}
|
{% set energy = state_attr('climate.thermostat_switch_1', 'total_energy') | float(default=-1) %}
|
||||||
{% if energy == 'unavailable' or energy is none%}unavailable{% else %}
|
{% if energy < 0 %}{{none}}{% else %}
|
||||||
{{ ((energy | float) / 1.0) | round(2, default=0) }}
|
{{ energy | round(2, default=0) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
- name: "Total énergie climate 2"
|
- name: "Total énergie climate 2"
|
||||||
unique_id: total_energie_climate2
|
unique_id: total_energie_climate2
|
||||||
unit_of_measurement: "kWh"
|
unit_of_measurement: "kWh"
|
||||||
device_class: energy
|
device_class: energy
|
||||||
state_class: total_increasing
|
state_class: total_increasing
|
||||||
state: >
|
state: >
|
||||||
{% set energy = state_attr('climate.thermostat_climate_2', 'total_energy') %}
|
{% set energy = state_attr('climate.thermostat_climate_2', 'total_energy') | float(default=-1) %}
|
||||||
{% if energy == 'unavailable' or energy is none%}unavailable{% else %}
|
{% if energy < 0 %}{{none}}{% else %}
|
||||||
{{ ((energy | float) / 1.0) | round(2, default=0) }}
|
{{ energy | round(2, default=0) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
- name: "Total énergie chambre"
|
- name: "Total énergie chambre"
|
||||||
unique_id: total_energie_chambre
|
unique_id: total_energie_chambre
|
||||||
unit_of_measurement: "kWh"
|
unit_of_measurement: "kWh"
|
||||||
device_class: energy
|
device_class: energy
|
||||||
state_class: total_increasing
|
state_class: total_increasing
|
||||||
state: >
|
state: >
|
||||||
{% set energy = state_attr('climate.thermostat_chambre', 'total_energy') %}
|
{% set energy = state_attr('climate.thermostat_chambre', 'total_energy') | float(default=-1) %}
|
||||||
{% if energy == 'unavailable' or energy is none%}unavailable{% else %}
|
{% if energy < 0 %}{{none}}{% else %}
|
||||||
{{ ((energy | float) / 1.0) | round(2, default=0) }}
|
{{ energy | round(2, default=0) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
switch:
|
switch:
|
||||||
- platform: template
|
- platform: template
|
||||||
switches:
|
switches:
|
||||||
pilote_sdb_rdc:
|
pilote_sdb_rdc:
|
||||||
friendly_name: "Pilote chauffage SDB RDC"
|
friendly_name: "Pilote chauffage SDB RDC"
|
||||||
value_template: "{{ is_state_attr('switch_seche_serviettes_sdb_rdc', 'sensor_state', 'on') }}"
|
value_template: "{{ is_state_attr('switch_seche_serviettes_sdb_rdc', 'sensor_state', 'on') }}"
|
||||||
turn_on:
|
turn_on:
|
||||||
service: select.select_option
|
service: select.select_option
|
||||||
data:
|
data:
|
||||||
option: comfort
|
option: comfort
|
||||||
target:
|
target:
|
||||||
entity_id: select.seche_serviettes_sdb_rdc_cable_outlet_mode
|
entity_id: select.seche_serviettes_sdb_rdc_cable_outlet_mode
|
||||||
|
|
||||||
turn_off:
|
turn_off:
|
||||||
service: select.select_option
|
service: select.select_option
|
||||||
data:
|
data:
|
||||||
option: comfort-2
|
option: comfort-2
|
||||||
target:
|
target:
|
||||||
entity_id: select.seche_serviettes_sdb_rdc_cable_outlet_mode
|
entity_id: select.seche_serviettes_sdb_rdc_cable_outlet_mode
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
themes:
|
themes:
|
||||||
versatile_thermostat_theme:
|
versatile_thermostat_theme:
|
||||||
state-binary_sensor-safety-on-color: "#FF0B0B"
|
state-binary_sensor-safety-on-color: "#FF0B0B"
|
||||||
state-binary_sensor-power-on-color: "#FF0B0B"
|
state-binary_sensor-power-on-color: "#FF0B0B"
|
||||||
state-binary_sensor-window-on-color: "rgb(156, 39, 176)"
|
state-binary_sensor-window-on-color: "rgb(156, 39, 176)"
|
||||||
state-binary_sensor-motion-on-color: "rgb(156, 39, 176)"
|
state-binary_sensor-motion-on-color: "rgb(156, 39, 176)"
|
||||||
state-binary_sensor-presence-on-color: "lightgreen"
|
state-binary_sensor-presence-on-color: "lightgreen"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ from homeassistant.core import (
|
|||||||
from homeassistant.components.climate import ClimateEntity
|
from homeassistant.components.climate import ClimateEntity
|
||||||
from homeassistant.helpers.restore_state import RestoreEntity
|
from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.helpers.entity import DeviceInfo, DeviceEntryType
|
from homeassistant.helpers.device_registry import DeviceInfo, DeviceEntryType
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.reload import async_setup_reload_service
|
from homeassistant.helpers.reload import async_setup_reload_service
|
||||||
@@ -505,7 +505,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
if len(presets):
|
if len(presets):
|
||||||
self._support_flags = SUPPORT_FLAGS | ClimateEntityFeature.PRESET_MODE
|
self._support_flags = SUPPORT_FLAGS | ClimateEntityFeature.PRESET_MODE
|
||||||
|
|
||||||
for key, val in CONF_PRESETS.items(): # TODO before presets.items():
|
for key, val in CONF_PRESETS.items():
|
||||||
if val != 0.0:
|
if val != 0.0:
|
||||||
self._attr_preset_modes.append(key)
|
self._attr_preset_modes.append(key)
|
||||||
|
|
||||||
@@ -1582,6 +1582,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
if not new_state:
|
if not new_state:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
new_hvac_mode = new_state.state
|
||||||
|
|
||||||
old_state = event.data.get("old_state")
|
old_state = event.data.get("old_state")
|
||||||
old_hvac_action = (
|
old_hvac_action = (
|
||||||
old_state.attributes.get("hvac_action")
|
old_state.attributes.get("hvac_action")
|
||||||
@@ -1594,16 +1596,21 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Issue 99 - some AC turn hvac_mode=cool and hvac_action=idle when sending a HVACMode_OFF command
|
||||||
|
if self._hvac_mode == HVACMode.OFF and new_hvac_action == HVACAction.IDLE:
|
||||||
|
_LOGGER.debug("The underlying switch to idle instead of OFF. We will consider it as OFF")
|
||||||
|
new_hvac_mode = HVACMode.OFF
|
||||||
|
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
"%s - Underlying climate changed. Event.new_state is %s, current_hvac_mode=%s, new_hvac_action=%s, old_hvac_action=%s",
|
"%s - Underlying climate changed. Event.new_hvac_mode is %s, current_hvac_mode=%s, new_hvac_action=%s, old_hvac_action=%s",
|
||||||
self,
|
self,
|
||||||
new_state,
|
new_hvac_mode,
|
||||||
self._hvac_mode,
|
self._hvac_mode,
|
||||||
new_hvac_action,
|
new_hvac_action,
|
||||||
old_hvac_action,
|
old_hvac_action,
|
||||||
)
|
)
|
||||||
|
|
||||||
if new_state.state in [
|
if new_hvac_mode in [
|
||||||
HVACMode.OFF,
|
HVACMode.OFF,
|
||||||
HVACMode.HEAT,
|
HVACMode.HEAT,
|
||||||
HVACMode.COOL,
|
HVACMode.COOL,
|
||||||
@@ -1611,8 +1618,9 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
HVACMode.DRY,
|
HVACMode.DRY,
|
||||||
HVACMode.AUTO,
|
HVACMode.AUTO,
|
||||||
HVACMode.FAN_ONLY,
|
HVACMode.FAN_ONLY,
|
||||||
|
None
|
||||||
]:
|
]:
|
||||||
self._hvac_mode = new_state.state
|
self._hvac_mode = new_hvac_mode
|
||||||
|
|
||||||
# Interpretation of hvac
|
# Interpretation of hvac
|
||||||
HVAC_ACTION_ON = [ # pylint: disable=invalid-name
|
HVAC_ACTION_ON = [ # pylint: disable=invalid-name
|
||||||
@@ -2098,9 +2106,18 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
switch_cond,
|
switch_cond,
|
||||||
)
|
)
|
||||||
|
|
||||||
ret = False
|
# Issue 99 - a climate is regulated by the device itself and not by VTherm. So a VTherm should never be in security !
|
||||||
if mode_cond and temp_cond and climate_cond:
|
shouldClimateBeInSecurity = False # temp_cond and climate_cond
|
||||||
if not self._security_state:
|
shouldSwitchBeInSecurity = temp_cond and switch_cond
|
||||||
|
shouldBeInSecurity = shouldClimateBeInSecurity or shouldSwitchBeInSecurity
|
||||||
|
|
||||||
|
shouldStartSecurity = mode_cond and not self._security_state and shouldBeInSecurity
|
||||||
|
# attr_preset_mode is not necessary normaly. It is just here to be sure
|
||||||
|
shouldStopSecurity = self._security_state and not shouldBeInSecurity and self._attr_preset_mode == PRESET_SECURITY
|
||||||
|
|
||||||
|
# Logging and event
|
||||||
|
if shouldStartSecurity:
|
||||||
|
if shouldClimateBeInSecurity:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f) and underlying climate is %s. Set it into security mode",
|
"%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f) and underlying climate is %s. Set it into security mode",
|
||||||
self,
|
self,
|
||||||
@@ -2109,10 +2126,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
delta_ext_temp,
|
delta_ext_temp,
|
||||||
self.hvac_action,
|
self.hvac_action,
|
||||||
)
|
)
|
||||||
ret = True
|
elif shouldSwitchBeInSecurity:
|
||||||
|
|
||||||
if mode_cond and temp_cond and switch_cond:
|
|
||||||
if not self._security_state:
|
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f) and on_percent (%.2f) is over defined value (%.2f). Set it into security mode",
|
"%s - No temperature received for more than %.1f minutes (dt=%.1f, dext=%.1f) and on_percent (%.2f) is over defined value (%.2f). Set it into security mode",
|
||||||
self,
|
self,
|
||||||
@@ -2122,9 +2136,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
self._prop_algorithm.on_percent,
|
self._prop_algorithm.on_percent,
|
||||||
self._security_min_on_percent,
|
self._security_min_on_percent,
|
||||||
)
|
)
|
||||||
ret = True
|
|
||||||
|
|
||||||
if mode_cond and temp_cond and not self._security_state:
|
|
||||||
self.send_event(
|
self.send_event(
|
||||||
EventType.TEMPERATURE_EVENT,
|
EventType.TEMPERATURE_EVENT,
|
||||||
{
|
{
|
||||||
@@ -2140,8 +2152,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if not self._security_state and ret:
|
if shouldStartSecurity:
|
||||||
self._security_state = ret
|
self._security_state = True
|
||||||
self.save_hvac_mode()
|
self.save_hvac_mode()
|
||||||
self.save_preset_mode()
|
self.save_preset_mode()
|
||||||
await self._async_set_preset_mode_internal(PRESET_SECURITY)
|
await self._async_set_preset_mode_internal(PRESET_SECURITY)
|
||||||
@@ -2167,18 +2179,14 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
if shouldStopSecurity:
|
||||||
self._security_state
|
|
||||||
and self._attr_preset_mode == PRESET_SECURITY
|
|
||||||
and not ret
|
|
||||||
):
|
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"%s - End of security mode. restoring hvac_mode to %s and preset_mode to %s",
|
"%s - End of security mode. restoring hvac_mode to %s and preset_mode to %s",
|
||||||
self,
|
self,
|
||||||
self._saved_hvac_mode,
|
self._saved_hvac_mode,
|
||||||
self._saved_preset_mode,
|
self._saved_preset_mode,
|
||||||
)
|
)
|
||||||
self._security_state = ret
|
self._security_state = False
|
||||||
# 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(False)
|
await self.restore_hvac_mode(False)
|
||||||
@@ -2201,7 +2209,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return ret
|
return shouldBeInSecurity
|
||||||
|
|
||||||
async def _async_control_heating(self, force=False, _=None):
|
async def _async_control_heating(self, force=False, _=None):
|
||||||
"""The main function used to run the calculation at each cycle"""
|
"""The main function used to run the calculation at each cycle"""
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ from datetime import timedelta
|
|||||||
from homeassistant.core import HomeAssistant, callback, Event
|
from homeassistant.core import HomeAssistant, callback, Event
|
||||||
from homeassistant.components.climate import ClimateEntity, DOMAIN as CLIMATE_DOMAIN
|
from homeassistant.components.climate import ClimateEntity, DOMAIN as CLIMATE_DOMAIN
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
from homeassistant.helpers.entity import Entity, DeviceInfo, DeviceEntryType
|
from homeassistant.helpers.entity import Entity
|
||||||
|
from homeassistant.helpers.device_registry import DeviceInfo, DeviceEntryType
|
||||||
from homeassistant.helpers.event import async_track_state_change_event, async_call_later
|
from homeassistant.helpers.event import async_track_state_change_event, async_call_later
|
||||||
|
|
||||||
from .climate import VersatileThermostat
|
from .climate import VersatileThermostat
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
homeassistant
|
homeassistant==2023.9.0
|
||||||
ffmpeg
|
ffmpeg
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# -r requirements_dev.txt
|
-r requirements_dev.txt
|
||||||
# aiodiscover
|
# aiodiscover
|
||||||
ulid_transform
|
ulid_transform
|
||||||
pytest-homeassistant-custom-component
|
pytest-homeassistant-custom-component
|
||||||
@@ -1,120 +1,121 @@
|
|||||||
reload:
|
reload:
|
||||||
description: Reload all Versatile Thermostat entities.
|
name: Reload
|
||||||
|
description: Reload all Versatile Thermostat entities.
|
||||||
|
|
||||||
set_presence:
|
set_presence:
|
||||||
name: Set presence
|
name: Set presence
|
||||||
description: Force the presence mode in thermostat
|
description: Force the presence mode in thermostat
|
||||||
target:
|
target:
|
||||||
entity:
|
entity:
|
||||||
integration: versatile_thermostat
|
integration: versatile_thermostat
|
||||||
fields:
|
fields:
|
||||||
presence:
|
presence:
|
||||||
name: Presence
|
name: Presence
|
||||||
description: Presence setting
|
description: Presence setting
|
||||||
required: true
|
required: true
|
||||||
advanced: false
|
advanced: false
|
||||||
example: "on"
|
example: "on"
|
||||||
default: "on"
|
default: "on"
|
||||||
selector:
|
selector:
|
||||||
select:
|
select:
|
||||||
options:
|
options:
|
||||||
- "on"
|
- "on"
|
||||||
- "off"
|
- "off"
|
||||||
- "home"
|
- "home"
|
||||||
- "not_home"
|
- "not_home"
|
||||||
|
|
||||||
set_preset_temperature:
|
set_preset_temperature:
|
||||||
name: Set temperature preset
|
name: Set temperature preset
|
||||||
description: Change the target temperature of a preset
|
description: Change the target temperature of a preset
|
||||||
target:
|
target:
|
||||||
entity:
|
entity:
|
||||||
integration: versatile_thermostat
|
integration: versatile_thermostat
|
||||||
fields:
|
fields:
|
||||||
preset:
|
preset:
|
||||||
name: Preset
|
name: Preset
|
||||||
description: Preset name
|
description: Preset name
|
||||||
required: true
|
required: true
|
||||||
advanced: false
|
advanced: false
|
||||||
example: "comfort"
|
example: "comfort"
|
||||||
selector:
|
selector:
|
||||||
select:
|
select:
|
||||||
options:
|
options:
|
||||||
- "eco"
|
- "eco"
|
||||||
- "comfort"
|
- "comfort"
|
||||||
- "boost"
|
- "boost"
|
||||||
temperature:
|
temperature:
|
||||||
name: Temperature when present
|
name: Temperature when present
|
||||||
description: Target temperature for the preset when present
|
description: Target temperature for the preset when present
|
||||||
required: false
|
required: false
|
||||||
advanced: false
|
advanced: false
|
||||||
example: "19.5"
|
example: "19.5"
|
||||||
default: "17"
|
default: "17"
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
min: 7
|
min: 7
|
||||||
max: 35
|
max: 35
|
||||||
step: 0.1
|
step: 0.1
|
||||||
unit_of_measurement: °
|
unit_of_measurement: °
|
||||||
mode: slider
|
mode: slider
|
||||||
temperature_away:
|
temperature_away:
|
||||||
name: Temperature when not present
|
name: Temperature when not present
|
||||||
description: Target temperature for the preset when not present
|
description: Target temperature for the preset when not present
|
||||||
required: false
|
required: false
|
||||||
advanced: false
|
advanced: false
|
||||||
example: "17"
|
example: "17"
|
||||||
default: "15"
|
default: "15"
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
min: 7
|
min: 7
|
||||||
max: 35
|
max: 35
|
||||||
step: 0.1
|
step: 0.1
|
||||||
unit_of_measurement: °
|
unit_of_measurement: °
|
||||||
mode: slider
|
mode: slider
|
||||||
|
|
||||||
set_security:
|
set_security:
|
||||||
name: Set security
|
name: Set security
|
||||||
description: Change the security parameters
|
description: Change the security parameters
|
||||||
target:
|
target:
|
||||||
entity:
|
entity:
|
||||||
integration: versatile_thermostat
|
integration: versatile_thermostat
|
||||||
fields:
|
fields:
|
||||||
delay_min:
|
delay_min:
|
||||||
name: Delay in minutes
|
name: Delay in minutes
|
||||||
description: Maximum allowed delay in minutes between two temperature mesures
|
description: Maximum allowed delay in minutes between two temperature mesures
|
||||||
required: false
|
required: false
|
||||||
advanced: false
|
advanced: false
|
||||||
example: "30"
|
example: "30"
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
min: 0
|
min: 0
|
||||||
max: 9999
|
max: 9999
|
||||||
unit_of_measurement: "min"
|
unit_of_measurement: "min"
|
||||||
mode: box
|
mode: box
|
||||||
min_on_percent:
|
min_on_percent:
|
||||||
name: Minimal on_percent
|
name: Minimal on_percent
|
||||||
description: Minimal heating percent value for security preset activation
|
description: Minimal heating percent value for security preset activation
|
||||||
required: false
|
required: false
|
||||||
advanced: false
|
advanced: false
|
||||||
example: "0.5"
|
example: "0.5"
|
||||||
default: "0.5"
|
default: "0.5"
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
min: 0
|
min: 0
|
||||||
max: 1
|
max: 1
|
||||||
step: 0.05
|
step: 0.05
|
||||||
unit_of_measurement: "%"
|
unit_of_measurement: "%"
|
||||||
mode: slider
|
mode: slider
|
||||||
default_on_percent:
|
default_on_percent:
|
||||||
name: on_percent used in security mode
|
name: on_percent used in security mode
|
||||||
description: The default heating percent value in security preset
|
description: The default heating percent value in security preset
|
||||||
required: false
|
required: false
|
||||||
advanced: false
|
advanced: false
|
||||||
example: "0.1"
|
example: "0.1"
|
||||||
default: "0.1"
|
default: "0.1"
|
||||||
selector:
|
selector:
|
||||||
number:
|
number:
|
||||||
min: 0
|
min: 0
|
||||||
max: 1
|
max: 1
|
||||||
step: 0.05
|
step: 0.05
|
||||||
unit_of_measurement: "%"
|
unit_of_measurement: "%"
|
||||||
mode: slider
|
mode: slider
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from custom_components.versatile_thermostat.const import (
|
|||||||
CONF_THERMOSTAT_CLIMATE,
|
CONF_THERMOSTAT_CLIMATE,
|
||||||
CONF_THERMOSTAT_SWITCH,
|
CONF_THERMOSTAT_SWITCH,
|
||||||
CONF_THERMOSTAT_TYPE,
|
CONF_THERMOSTAT_TYPE,
|
||||||
|
CONF_AC_MODE,
|
||||||
CONF_TEMP_SENSOR,
|
CONF_TEMP_SENSOR,
|
||||||
CONF_EXTERNAL_TEMP_SENSOR,
|
CONF_EXTERNAL_TEMP_SENSOR,
|
||||||
CONF_CYCLE_MIN,
|
CONF_CYCLE_MIN,
|
||||||
@@ -112,6 +113,7 @@ MOCK_TH_OVER_SWITCH_TPI_CONFIG = {
|
|||||||
|
|
||||||
MOCK_TH_OVER_CLIMATE_TYPE_CONFIG = {
|
MOCK_TH_OVER_CLIMATE_TYPE_CONFIG = {
|
||||||
CONF_CLIMATE: "climate.mock_climate",
|
CONF_CLIMATE: "climate.mock_climate",
|
||||||
|
CONF_AC_MODE: False,
|
||||||
}
|
}
|
||||||
|
|
||||||
MOCK_PRESETS_CONFIG = {
|
MOCK_PRESETS_CONFIG = {
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ async def test_window_management_time_not_enough(
|
|||||||
await try_window_condition(None)
|
await try_window_condition(None)
|
||||||
assert entity.window_state == STATE_OFF
|
assert entity.window_state == STATE_OFF
|
||||||
|
|
||||||
await entity.remove_thermostat()
|
entity.remove_thermostat()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||||
@@ -234,7 +234,7 @@ async def test_window_management_time_enough(
|
|||||||
assert entity.preset_mode is PRESET_BOOST
|
assert entity.preset_mode is PRESET_BOOST
|
||||||
|
|
||||||
# Clean the entity
|
# Clean the entity
|
||||||
await entity.remove_thermostat()
|
entity.remove_thermostat()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||||
@@ -418,7 +418,7 @@ async def test_window_auto_fast(hass: HomeAssistant, skip_hass_states_is_state):
|
|||||||
assert entity.hvac_mode is HVACMode.HEAT
|
assert entity.hvac_mode is HVACMode.HEAT
|
||||||
|
|
||||||
# Clean the entity
|
# Clean the entity
|
||||||
await entity.remove_thermostat()
|
entity.remove_thermostat()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||||
@@ -561,7 +561,7 @@ async def test_window_auto_auto_stop(hass: HomeAssistant, skip_hass_states_is_st
|
|||||||
assert entity.preset_mode is PRESET_BOOST
|
assert entity.preset_mode is PRESET_BOOST
|
||||||
|
|
||||||
# Clean the entity
|
# Clean the entity
|
||||||
await entity.remove_thermostat()
|
entity.remove_thermostat()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
@pytest.mark.parametrize("expected_lingering_tasks", [True])
|
||||||
@@ -670,4 +670,4 @@ async def test_window_auto_no_on_percent(
|
|||||||
assert entity.hvac_mode is HVACMode.HEAT
|
assert entity.hvac_mode is HVACMode.HEAT
|
||||||
|
|
||||||
# Clean the entity
|
# Clean the entity
|
||||||
await entity.remove_thermostat()
|
entity.remove_thermostat()
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ class UnderlyingSwitch(UnderlyingEntity):
|
|||||||
self._on_time_sec,
|
self._on_time_sec,
|
||||||
)
|
)
|
||||||
|
|
||||||
await self._cancel_cycle()
|
self._cancel_cycle()
|
||||||
|
|
||||||
if self._hvac_mode == HVACMode.OFF:
|
if self._hvac_mode == HVACMode.OFF:
|
||||||
_LOGGER.debug("%s - End of cycle (HVAC_MODE_OFF - 2)", self)
|
_LOGGER.debug("%s - End of cycle (HVAC_MODE_OFF - 2)", self)
|
||||||
@@ -327,7 +327,7 @@ class UnderlyingSwitch(UnderlyingEntity):
|
|||||||
self._should_relaunch_control_heating,
|
self._should_relaunch_control_heating,
|
||||||
self._off_time_sec,
|
self._off_time_sec,
|
||||||
)
|
)
|
||||||
await self._cancel_cycle()
|
self._cancel_cycle()
|
||||||
|
|
||||||
if self._hvac_mode == HVACMode.OFF:
|
if self._hvac_mode == HVACMode.OFF:
|
||||||
_LOGGER.debug("%s - End of cycle (HVAC_MODE_OFF - 2)", self)
|
_LOGGER.debug("%s - End of cycle (HVAC_MODE_OFF - 2)", self)
|
||||||
@@ -446,7 +446,7 @@ class UnderlyingClimate(UnderlyingEntity):
|
|||||||
def is_device_active(self):
|
def is_device_active(self):
|
||||||
"""If the toggleable device is currently active."""
|
"""If the toggleable device is currently active."""
|
||||||
if self.is_initialized:
|
if self.is_initialized:
|
||||||
return self._underlying_climate.hvac_action not in [
|
return self._underlying_climate.hvac_mode != HVACMode.OFF and self._underlying_climate.hvac_action not in [
|
||||||
HVACAction.IDLE,
|
HVACAction.IDLE,
|
||||||
HVACAction.OFF,
|
HVACAction.OFF,
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user