76 files
This commit is contained in:
@@ -256,7 +256,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "8713ea9c008e4270bb28085eed5be959",
|
"id": "8713ea9c008e4270bb28085eed5be959",
|
||||||
"url": "/hacsfiles/Statistics-Graph-Chart-Card/statistics-graph-chart-card.js?hacstag=1034932856401",
|
"url": "/hacsfiles/Statistics-Graph-Chart-Card/statistics-graph-chart-card.js?hacstag=1034932856402",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,5 +9,5 @@
|
|||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"issue_tracker": "https://github.com/lovelylain/hass_ingress/issues",
|
"issue_tracker": "https://github.com/lovelylain/hass_ingress/issues",
|
||||||
"single_config_entry": true,
|
"single_config_entry": true,
|
||||||
"version": "1.3.0"
|
"version": "1.3.2"
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -17,6 +17,12 @@ _LOGGER = get_vtherm_logger(__name__)
|
|||||||
class BaseFeatureManager:
|
class BaseFeatureManager:
|
||||||
"""A base class for all feature"""
|
"""A base class for all feature"""
|
||||||
|
|
||||||
|
# Attributes that should be excluded from the recorder history. Each manager
|
||||||
|
# exposes its custom attributes under a dedicated top-level section key; that
|
||||||
|
# section name is declared here so the recorder skips the whole section
|
||||||
|
# (the recorder can only filter top-level keys, not nested ones).
|
||||||
|
unrecorded_attributes = frozenset()
|
||||||
|
|
||||||
def __init__(self, vtherm: Any, hass: HomeAssistant, name: str = None):
|
def __init__(self, vtherm: Any, hass: HomeAssistant, name: str = None):
|
||||||
"""Init of a featureManager"""
|
"""Init of a featureManager"""
|
||||||
self._vtherm = vtherm
|
self._vtherm = vtherm
|
||||||
|
|||||||
@@ -85,11 +85,16 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
|
|||||||
_attr_swing_horizontal_mode = ""
|
_attr_swing_horizontal_mode = ""
|
||||||
|
|
||||||
_entity_component_unrecorded_attributes = (
|
_entity_component_unrecorded_attributes = (
|
||||||
ClimateEntity._entity_component_unrecorded_attributes.union(frozenset({"configuration", "preset_temperatures"}))
|
ClimateEntity._entity_component_unrecorded_attributes.union(frozenset({"configuration", "preset_temperatures", "specific_states"}))
|
||||||
.union(FeaturePresenceManager.unrecorded_attributes)
|
.union(FeaturePresenceManager.unrecorded_attributes)
|
||||||
.union(FeaturePowerManager.unrecorded_attributes)
|
.union(FeaturePowerManager.unrecorded_attributes)
|
||||||
.union(FeatureMotionManager.unrecorded_attributes)
|
.union(FeatureMotionManager.unrecorded_attributes)
|
||||||
.union(FeatureWindowManager.unrecorded_attributes)
|
.union(FeatureWindowManager.unrecorded_attributes)
|
||||||
|
.union(FeatureSafetyManager.unrecorded_attributes)
|
||||||
|
.union(FeatureLockManager.unrecorded_attributes)
|
||||||
|
.union(FeatureTimedPresetManager.unrecorded_attributes)
|
||||||
|
.union(FeatureHeatingFailureDetectionManager.unrecorded_attributes)
|
||||||
|
.union(FeatureRepairIncorrectStateManager.unrecorded_attributes)
|
||||||
)
|
)
|
||||||
|
|
||||||
##
|
##
|
||||||
@@ -192,6 +197,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
|
|||||||
self._use_central_config_temperature = False
|
self._use_central_config_temperature = False
|
||||||
|
|
||||||
self._hvac_off_reason: str | None = None
|
self._hvac_off_reason: str | None = None
|
||||||
|
self._hvac_mode_reason: str | None = None
|
||||||
self._hvac_list: list[VThermHvacMode] = []
|
self._hvac_list: list[VThermHvacMode] = []
|
||||||
self._str_hvac_list: list[str] = []
|
self._str_hvac_list: list[str] = []
|
||||||
self._temperature_reason: str | None = None
|
self._temperature_reason: str | None = None
|
||||||
@@ -634,6 +640,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
|
|||||||
|
|
||||||
# Try to get total_energy from specific_states (new format) or root level (old format)
|
# Try to get total_energy from specific_states (new format) or root level (old format)
|
||||||
specific_states = old_state.attributes.get("specific_states", {})
|
specific_states = old_state.attributes.get("specific_states", {})
|
||||||
|
self._hvac_mode_reason = specific_states.get(HVAC_MODE_REASON_NAME, self._hvac_off_reason)
|
||||||
old_total_energy = specific_states.get(ATTR_TOTAL_ENERGY)
|
old_total_energy = specific_states.get(ATTR_TOTAL_ENERGY)
|
||||||
if old_total_energy is None:
|
if old_total_energy is None:
|
||||||
# Fallback to root level for backward compatibility
|
# Fallback to root level for backward compatibility
|
||||||
@@ -1280,6 +1287,14 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
|
|||||||
window detection or auto-start-stop"""
|
window detection or auto-start-stop"""
|
||||||
return self._hvac_off_reason
|
return self._hvac_off_reason
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hvac_mode_reason(self) -> str | None:
|
||||||
|
"""Returns the reason why the current hvac_mode is forced.
|
||||||
|
Unlike hvac_off_reason, this is set whatever the forced hvac_mode is
|
||||||
|
(off, fan_only, dry, ...) so the UI can explain why the VTherm is not
|
||||||
|
in the requested mode."""
|
||||||
|
return self._hvac_mode_reason
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def temperature_reason(self) -> str | None:
|
def temperature_reason(self) -> str | None:
|
||||||
"""Returns the reason of the target temperature
|
"""Returns the reason of the target temperature
|
||||||
@@ -1680,6 +1695,9 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
|
|||||||
# issue #1958 - when window_action=fan_only temporarily switches the mode to FAN_ONLY,
|
# issue #1958 - when window_action=fan_only temporarily switches the mode to FAN_ONLY,
|
||||||
# the user's intent (requested_state) is still COOL, so we must use AC presets.
|
# the user's intent (requested_state) is still COOL, so we must use AC presets.
|
||||||
or (self.vtherm_hvac_mode == VThermHvacMode_FAN_ONLY and self.requested_state.hvac_mode == VThermHvacMode_COOL)
|
or (self.vtherm_hvac_mode == VThermHvacMode_FAN_ONLY and self.requested_state.hvac_mode == VThermHvacMode_COOL)
|
||||||
|
# when auto-start/stop temporarily switches the mode to DRY as its stop mode,
|
||||||
|
# the user's intent (requested_state) is still COOL, so we must use AC presets.
|
||||||
|
or (self.vtherm_hvac_mode == VThermHvacMode_DRY and self.requested_state.hvac_mode == VThermHvacMode_COOL)
|
||||||
# (self.is_over_switch and self._ac_mode)
|
# (self.is_over_switch and self._ac_mode)
|
||||||
# or self.vtherm_hvac_mode == VThermHvacMode_COOL
|
# or self.vtherm_hvac_mode == VThermHvacMode_COOL
|
||||||
# or (self.vtherm_hvac_mode == VThermHvacMode_OFF and self.requested_state.hvac_mode == VThermHvacMode_COOL)
|
# or (self.vtherm_hvac_mode == VThermHvacMode_OFF and self.requested_state.hvac_mode == VThermHvacMode_COOL)
|
||||||
@@ -1733,6 +1751,10 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
|
|||||||
"""Set the reason of hvac_off"""
|
"""Set the reason of hvac_off"""
|
||||||
self._hvac_off_reason = hvac_off_reason
|
self._hvac_off_reason = hvac_off_reason
|
||||||
|
|
||||||
|
def set_hvac_mode_reason(self, hvac_mode_reason: str | None):
|
||||||
|
"""Set the reason why the current hvac_mode is forced"""
|
||||||
|
self._hvac_mode_reason = hvac_mode_reason
|
||||||
|
|
||||||
def set_temperature_reason(self, temperature_reason: str | None):
|
def set_temperature_reason(self, temperature_reason: str | None):
|
||||||
"""Set the reason of temperature"""
|
"""Set the reason of temperature"""
|
||||||
self._temperature_reason = temperature_reason
|
self._temperature_reason = temperature_reason
|
||||||
@@ -1823,6 +1845,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
|
|||||||
"ema_temp": self._ema_temp,
|
"ema_temp": self._ema_temp,
|
||||||
"temperature_slope": round(self.last_temperature_slope or 0, 3),
|
"temperature_slope": round(self.last_temperature_slope or 0, 3),
|
||||||
"hvac_off_reason": self.hvac_off_reason,
|
"hvac_off_reason": self.hvac_off_reason,
|
||||||
|
"hvac_mode_reason": self.hvac_mode_reason,
|
||||||
ATTR_TOTAL_ENERGY: self.total_energy,
|
ATTR_TOTAL_ENERGY: self.total_energy,
|
||||||
"last_change_time_from_vtherm": (
|
"last_change_time_from_vtherm": (
|
||||||
self._last_change_time_from_vtherm.astimezone(self._current_tz).isoformat() if self._last_change_time_from_vtherm is not None else None
|
self._last_change_time_from_vtherm.astimezone(self._current_tz).isoformat() if self._last_change_time_from_vtherm is not None else None
|
||||||
@@ -2134,7 +2157,13 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
|
|||||||
"This thermostat does not use TPI algorithm."
|
"This thermostat does not use TPI algorithm."
|
||||||
)
|
)
|
||||||
|
|
||||||
async def service_set_auto_tpi_mode(self, auto_tpi_mode: bool):
|
async def service_set_auto_tpi_mode(
|
||||||
|
self,
|
||||||
|
auto_tpi_mode: bool,
|
||||||
|
reinitialise: bool = True,
|
||||||
|
allow_kint_boost_on_stagnation: bool = False,
|
||||||
|
allow_kext_compensation_on_overshoot: bool = False,
|
||||||
|
):
|
||||||
"""Stub method for Auto TPI mode service on non-TPI thermostats.
|
"""Stub method for Auto TPI mode service on non-TPI thermostats.
|
||||||
|
|
||||||
This service is only available for switch/valve type thermostats that use TPI algorithm.
|
This service is only available for switch/valve type thermostats that use TPI algorithm.
|
||||||
|
|||||||
@@ -50,13 +50,14 @@ DOMAIN = "versatile_thermostat"
|
|||||||
|
|
||||||
# The order is important.
|
# The order is important.
|
||||||
PLATFORMS: list[Platform] = [
|
PLATFORMS: list[Platform] = [
|
||||||
Platform.SELECT,
|
|
||||||
Platform.CLIMATE,
|
Platform.CLIMATE,
|
||||||
Platform.SENSOR,
|
Platform.SENSOR,
|
||||||
# Number should be after CLIMATE
|
# Number should be after CLIMATE
|
||||||
Platform.NUMBER,
|
Platform.NUMBER,
|
||||||
Platform.BINARY_SENSOR,
|
Platform.BINARY_SENSOR,
|
||||||
Platform.SWITCH,
|
Platform.SWITCH,
|
||||||
|
# Select should be after CLIMATE
|
||||||
|
Platform.SELECT,
|
||||||
]
|
]
|
||||||
|
|
||||||
CONF_UNDERLYING_LIST = "underlying_entity_ids"
|
CONF_UNDERLYING_LIST = "underlying_entity_ids"
|
||||||
@@ -244,7 +245,18 @@ TYPE_AUTO_START_STOP_LEVELS = Literal[ # pylint: disable=invalid-name
|
|||||||
AUTO_START_STOP_LEVEL_NONE,
|
AUTO_START_STOP_LEVEL_NONE,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# The hvac_mode applied when the auto-start/stop feature detects a stop condition
|
||||||
|
AUTO_START_STOP_STOP_MODE_OFF = str(VThermHvacMode_OFF)
|
||||||
|
AUTO_START_STOP_STOP_MODE_FAN_ONLY = str(VThermHvacMode_FAN_ONLY)
|
||||||
|
AUTO_START_STOP_STOP_MODE_DRY = str(VThermHvacMode_DRY)
|
||||||
|
AUTO_START_STOP_STOP_MODES = [
|
||||||
|
AUTO_START_STOP_STOP_MODE_OFF,
|
||||||
|
AUTO_START_STOP_STOP_MODE_FAN_ONLY,
|
||||||
|
AUTO_START_STOP_STOP_MODE_DRY,
|
||||||
|
]
|
||||||
|
|
||||||
HVAC_OFF_REASON_NAME = "hvac_off_reason"
|
HVAC_OFF_REASON_NAME = "hvac_off_reason"
|
||||||
|
HVAC_MODE_REASON_NAME = "hvac_mode_reason"
|
||||||
HVAC_OFF_REASON_MANUAL = "hvac_off_manual"
|
HVAC_OFF_REASON_MANUAL = "hvac_off_manual"
|
||||||
HVAC_OFF_REASON_AUTO_START_STOP = "hvac_off_auto_start_stop"
|
HVAC_OFF_REASON_AUTO_START_STOP = "hvac_off_auto_start_stop"
|
||||||
HVAC_OFF_REASON_WINDOW_DETECTION = "hvac_off_window_detection"
|
HVAC_OFF_REASON_WINDOW_DETECTION = "hvac_off_window_detection"
|
||||||
@@ -255,6 +267,15 @@ HVAC_OFF_REASONS = Literal[ # pylint: disable=invalid-name
|
|||||||
HVAC_OFF_REASON_MANUAL, HVAC_OFF_REASON_AUTO_START_STOP, HVAC_OFF_REASON_WINDOW_DETECTION, HVAC_OFF_REASON_SLEEP_MODE, HVAC_OFF_REASON_SAFETY
|
HVAC_OFF_REASON_MANUAL, HVAC_OFF_REASON_AUTO_START_STOP, HVAC_OFF_REASON_WINDOW_DETECTION, HVAC_OFF_REASON_SLEEP_MODE, HVAC_OFF_REASON_SAFETY
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# The hvac_mode_reason set when the auto-start/stop feature applies a non-off stop mode
|
||||||
|
HVAC_MODE_REASON_AUTO_START_STOP_FAN_ONLY = "hvac_fan_only_auto_start_stop"
|
||||||
|
HVAC_MODE_REASON_AUTO_START_STOP_DRY = "hvac_dry_auto_start_stop"
|
||||||
|
# Maps the stop mode applied by the auto-start/stop feature to the related hvac_mode_reason
|
||||||
|
AUTO_START_STOP_HVAC_MODE_REASONS = {
|
||||||
|
AUTO_START_STOP_STOP_MODE_FAN_ONLY: HVAC_MODE_REASON_AUTO_START_STOP_FAN_ONLY,
|
||||||
|
AUTO_START_STOP_STOP_MODE_DRY: HVAC_MODE_REASON_AUTO_START_STOP_DRY,
|
||||||
|
}
|
||||||
|
|
||||||
DEFAULT_SHORT_EMA_PARAMS = {
|
DEFAULT_SHORT_EMA_PARAMS = {
|
||||||
"max_alpha": 0.5,
|
"max_alpha": 0.5,
|
||||||
# In sec
|
# In sec
|
||||||
@@ -502,7 +523,7 @@ ATTR_TOTAL_ENERGY = "total_energy"
|
|||||||
ATTR_MEAN_POWER_CYCLE = "mean_cycle_power"
|
ATTR_MEAN_POWER_CYCLE = "mean_cycle_power"
|
||||||
|
|
||||||
AUTO_FAN_DTEMP_THRESHOLD = 2
|
AUTO_FAN_DTEMP_THRESHOLD = 2
|
||||||
AUTO_FAN_DEACTIVATED_MODES = ["mute", "quiet", "low", "quiet", "1", "auto"]
|
AUTO_FAN_DEACTIVATED_MODES = ["mute", "quiet", "low", "quiet", "1", "one", "speed_1", "auto"]
|
||||||
|
|
||||||
CENTRAL_CONFIG_NAME = "Central configuration"
|
CENTRAL_CONFIG_NAME = "Central configuration"
|
||||||
|
|
||||||
|
|||||||
@@ -32,12 +32,8 @@ class FeatureAutoStartStopManager(BaseFeatureManager):
|
|||||||
|
|
||||||
unrecorded_attributes = frozenset(
|
unrecorded_attributes = frozenset(
|
||||||
{
|
{
|
||||||
"auto_start_stop_level",
|
"is_auto_start_stop_configured",
|
||||||
"auto_start_stop_dtmin",
|
"auto_start_stop_manager",
|
||||||
"auto_start_stop_enable",
|
|
||||||
"auto_start_stop_accumulated_error",
|
|
||||||
"auto_start_stop_accumulated_error_threshold",
|
|
||||||
"auto_start_stop_last_switch_date",
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -50,6 +46,7 @@ class FeatureAutoStartStopManager(BaseFeatureManager):
|
|||||||
self._is_configured: bool = False
|
self._is_configured: bool = False
|
||||||
self._is_auto_start_stop_enabled: bool = False
|
self._is_auto_start_stop_enabled: bool = False
|
||||||
self._is_auto_stop_detected: bool = False
|
self._is_auto_stop_detected: bool = False
|
||||||
|
self._stop_mode: str = AUTO_START_STOP_STOP_MODE_OFF
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
def post_init(self, entry_infos: ConfigData):
|
def post_init(self, entry_infos: ConfigData):
|
||||||
@@ -126,7 +123,7 @@ class FeatureAutoStartStopManager(BaseFeatureManager):
|
|||||||
"type": "stop",
|
"type": "stop",
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"cause": "Auto stop conditions reached",
|
"cause": "Auto stop conditions reached",
|
||||||
"hvac_mode": str(VThermHvacMode_OFF),
|
"hvac_mode": str(self.stop_mode),
|
||||||
"saved_hvac_mode": str(self._vtherm.requested_state.hvac_mode),
|
"saved_hvac_mode": str(self._vtherm.requested_state.hvac_mode),
|
||||||
"target_temperature": self._vtherm.target_temperature,
|
"target_temperature": self._vtherm.target_temperature,
|
||||||
"current_temperature": self._vtherm.current_temperature,
|
"current_temperature": self._vtherm.current_temperature,
|
||||||
@@ -207,6 +204,25 @@ class FeatureAutoStartStopManager(BaseFeatureManager):
|
|||||||
|
|
||||||
self._vtherm.update_custom_attributes()
|
self._vtherm.update_custom_attributes()
|
||||||
|
|
||||||
|
async def set_auto_start_stop_stop_mode(self, stop_mode: str):
|
||||||
|
"""Set the hvac_mode to apply when a stop is detected (off/fan_only/dry).
|
||||||
|
|
||||||
|
If a stop is currently active, the new mode is applied immediately
|
||||||
|
while the stop state is kept."""
|
||||||
|
if self._stop_mode == stop_mode:
|
||||||
|
return
|
||||||
|
|
||||||
|
write_event_log(_LOGGER, self._vtherm, f"Auto start/stop stop mode changed from {self._stop_mode} to {stop_mode}")
|
||||||
|
self._stop_mode = stop_mode
|
||||||
|
|
||||||
|
# If a stop is currently active, re-evaluate the state so the hvac_mode
|
||||||
|
# reflects the new choice immediately while keeping the stop state.
|
||||||
|
if self._is_auto_stop_detected:
|
||||||
|
self._vtherm.requested_state.force_changed()
|
||||||
|
await self._vtherm.update_states(force=True)
|
||||||
|
|
||||||
|
self._vtherm.update_custom_attributes()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
@overrides
|
@overrides
|
||||||
def restore_state(self, old_state) -> None:
|
def restore_state(self, old_state) -> None:
|
||||||
@@ -272,6 +288,7 @@ class FeatureAutoStartStopManager(BaseFeatureManager):
|
|||||||
"auto_start_stop_accumulated_error_threshold": self._auto_start_stop_algo.accumulated_error_threshold,
|
"auto_start_stop_accumulated_error_threshold": self._auto_start_stop_algo.accumulated_error_threshold,
|
||||||
"auto_start_stop_last_switch_date": self._auto_start_stop_algo.last_switch_date,
|
"auto_start_stop_last_switch_date": self._auto_start_stop_algo.last_switch_date,
|
||||||
"is_auto_stop_detected": self.is_auto_stop_detected,
|
"is_auto_stop_detected": self.is_auto_stop_detected,
|
||||||
|
"auto_start_stop_stop_mode": self._stop_mode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -302,6 +319,11 @@ class FeatureAutoStartStopManager(BaseFeatureManager):
|
|||||||
"""Returns the auto_start_stop_enable"""
|
"""Returns the auto_start_stop_enable"""
|
||||||
return self._is_auto_start_stop_enabled
|
return self._is_auto_start_stop_enabled
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stop_mode(self) -> VThermHvacMode:
|
||||||
|
"""Return the hvac_mode to apply when a stop is detected (off/fan_only/dry)"""
|
||||||
|
return VThermHvacMode(self._stop_mode)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_auto_stopped(self) -> bool:
|
def is_auto_stopped(self) -> bool:
|
||||||
"""Returns the is vtherm is stopped and reason is AUTO_START_STOP"""
|
"""Returns the is vtherm is stopped and reason is AUTO_START_STOP"""
|
||||||
|
|||||||
@@ -54,12 +54,8 @@ class FeatureHeatingFailureDetectionManager(BaseFeatureManager):
|
|||||||
|
|
||||||
unrecorded_attributes = frozenset(
|
unrecorded_attributes = frozenset(
|
||||||
{
|
{
|
||||||
"heating_failure_threshold",
|
|
||||||
"cooling_failure_threshold",
|
|
||||||
"heating_failure_detection_delay",
|
|
||||||
"temperature_change_tolerance",
|
|
||||||
"is_heating_failure_detection_configured",
|
"is_heating_failure_detection_configured",
|
||||||
"failure_detection_enable_template",
|
"heating_failure_detection_manager",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,14 @@ _LOGGER = get_vtherm_logger(__name__)
|
|||||||
|
|
||||||
class FeatureLockManager(BaseFeatureManager):
|
class FeatureLockManager(BaseFeatureManager):
|
||||||
""" The implementation of the Lock Feature Manager for Versatile Thermostat """
|
""" The implementation of the Lock Feature Manager for Versatile Thermostat """
|
||||||
|
|
||||||
|
unrecorded_attributes = frozenset(
|
||||||
|
{
|
||||||
|
"is_lock_configured",
|
||||||
|
"lock_manager",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, vtherm: Any, hass: HomeAssistant):
|
def __init__(self, vtherm: Any, hass: HomeAssistant):
|
||||||
"""Initialize the FeatureLockManager."""
|
"""Initialize the FeatureLockManager."""
|
||||||
super().__init__(vtherm, hass)
|
super().__init__(vtherm, hass)
|
||||||
|
|||||||
@@ -42,12 +42,8 @@ class FeatureMotionManager(BaseFeatureManager):
|
|||||||
|
|
||||||
unrecorded_attributes = frozenset(
|
unrecorded_attributes = frozenset(
|
||||||
{
|
{
|
||||||
"motion_sensor_entity_id",
|
|
||||||
"is_motion_configured",
|
"is_motion_configured",
|
||||||
"motion_delay_sec",
|
"motion_manager",
|
||||||
"motion_off_delay_sec",
|
|
||||||
"motion_preset",
|
|
||||||
"no_motion_preset",
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -31,13 +31,8 @@ class FeaturePowerManager(BaseFeatureManager):
|
|||||||
|
|
||||||
unrecorded_attributes = frozenset(
|
unrecorded_attributes = frozenset(
|
||||||
{
|
{
|
||||||
"power_sensor_entity_id",
|
|
||||||
"max_power_sensor_entity_id",
|
|
||||||
"is_power_configured",
|
"is_power_configured",
|
||||||
"device_power",
|
"power_manager",
|
||||||
"power_temp",
|
|
||||||
"current_power",
|
|
||||||
"current_max_power",
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ class FeaturePresenceManager(BaseFeatureManager):
|
|||||||
|
|
||||||
unrecorded_attributes = frozenset(
|
unrecorded_attributes = frozenset(
|
||||||
{
|
{
|
||||||
"presence_sensor_entity_id",
|
|
||||||
"is_presence_configured",
|
"is_presence_configured",
|
||||||
|
"presence_manager",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ class FeatureRepairIncorrectStateManager(BaseFeatureManager):
|
|||||||
unrecorded_attributes = frozenset(
|
unrecorded_attributes = frozenset(
|
||||||
{
|
{
|
||||||
"is_repair_incorrect_state_configured",
|
"is_repair_incorrect_state_configured",
|
||||||
|
"repair_incorrect_state_manager",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -32,10 +32,8 @@ class FeatureSafetyManager(BaseFeatureManager):
|
|||||||
|
|
||||||
unrecorded_attributes = frozenset(
|
unrecorded_attributes = frozenset(
|
||||||
{
|
{
|
||||||
"safety_delay_min",
|
|
||||||
"safety_min_on_percent",
|
|
||||||
"safety_default_on_percent",
|
|
||||||
"is_safety_configured",
|
"is_safety_configured",
|
||||||
|
"safety_manager",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -43,15 +43,8 @@ class FeatureWindowManager(BaseFeatureManager):
|
|||||||
|
|
||||||
unrecorded_attributes = frozenset(
|
unrecorded_attributes = frozenset(
|
||||||
{
|
{
|
||||||
"window_sensor_entity_id",
|
|
||||||
"is_window_configured",
|
"is_window_configured",
|
||||||
"window_delay_sec",
|
"window_manager",
|
||||||
"window_off_delay_sec",
|
|
||||||
"window_auto_configured",
|
|
||||||
"window_auto_open_threshold",
|
|
||||||
"window_auto_close_threshold",
|
|
||||||
"window_auto_max_duration",
|
|
||||||
"window_action",
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -134,7 +127,8 @@ class FeatureWindowManager(BaseFeatureManager):
|
|||||||
|
|
||||||
# Try to get last window bypass state
|
# Try to get last window bypass state
|
||||||
old_state = await self._vtherm.async_get_last_state()
|
old_state = await self._vtherm.async_get_last_state()
|
||||||
self._is_window_bypass = old_state is not None and hasattr(old_state, "attributes") and old_state.attributes.get("is_window_bypass") is True
|
old_attributes = getattr(old_state, "attributes", None) or {}
|
||||||
|
self._is_window_bypass = (old_attributes.get("window_manager") or {}).get("is_window_bypass") is True
|
||||||
|
|
||||||
if self._is_configured:
|
if self._is_configured:
|
||||||
self.stop_listening()
|
self.stop_listening()
|
||||||
|
|||||||
@@ -22,6 +22,6 @@
|
|||||||
"vtherm_api>=0.3.0"
|
"vtherm_api>=0.3.0"
|
||||||
],
|
],
|
||||||
"ssdp": [],
|
"ssdp": [],
|
||||||
"version": "10.0.2",
|
"version": "10.1.0",
|
||||||
"zeroconf": []
|
"zeroconf": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,8 +86,11 @@ class PITemperatureRegulator:
|
|||||||
# Calculate the sum of error (I)
|
# Calculate the sum of error (I)
|
||||||
# Discussion #384. Finally don't reset the accumulated error but smoothly reset it if the sign is inversed
|
# Discussion #384. Finally don't reset the accumulated error but smoothly reset it if the sign is inversed
|
||||||
# If the error have change its sign, reset smoothly the accumulated error
|
# If the error have change its sign, reset smoothly the accumulated error
|
||||||
|
# The divisor is clamped so that a fractional cycle (time_delta < 0.5), which happens when
|
||||||
|
# the regulation is triggered twice in quick succession (e.g. repeated target changes),
|
||||||
|
# can never amplify the accumulated error instead of decaying it.
|
||||||
if self.overheat_protection and error * self.accumulated_error < 0:
|
if self.overheat_protection and error * self.accumulated_error < 0:
|
||||||
self.accumulated_error = self.accumulated_error / (2.0 * time_delta)
|
self.accumulated_error = self.accumulated_error / (2.0 * max(time_delta, 0.5))
|
||||||
|
|
||||||
self.accumulated_error += error * time_delta
|
self.accumulated_error += error * time_delta
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
import logging
|
import logging
|
||||||
from vtherm_api.log_collector import get_vtherm_logger
|
from vtherm_api.log_collector import get_vtherm_logger
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant, callback, Event
|
||||||
|
|
||||||
|
from homeassistant.const import EntityCategory
|
||||||
from homeassistant.components.select import SelectEntity
|
from homeassistant.components.select import SelectEntity
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo, DeviceEntryType
|
from homeassistant.helpers.device_registry import DeviceInfo, DeviceEntryType
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
@@ -18,14 +19,22 @@ from custom_components.versatile_thermostat.base_thermostat import (
|
|||||||
|
|
||||||
from custom_components.versatile_thermostat.vtherm_central_api import VersatileThermostatAPI
|
from custom_components.versatile_thermostat.vtherm_central_api import VersatileThermostatAPI
|
||||||
|
|
||||||
|
from .base_entity import VersatileThermostatBaseEntity
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
DEVICE_MANUFACTURER,
|
DEVICE_MANUFACTURER,
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
CONF_THERMOSTAT_TYPE,
|
CONF_THERMOSTAT_TYPE,
|
||||||
CONF_THERMOSTAT_CENTRAL_CONFIG,
|
CONF_THERMOSTAT_CENTRAL_CONFIG,
|
||||||
|
CONF_THERMOSTAT_CLIMATE,
|
||||||
|
CONF_USE_AUTO_START_STOP_FEATURE,
|
||||||
CENTRAL_MODE_AUTO,
|
CENTRAL_MODE_AUTO,
|
||||||
CENTRAL_MODES,
|
CENTRAL_MODES,
|
||||||
|
AUTO_START_STOP_STOP_MODE_OFF,
|
||||||
|
AUTO_START_STOP_STOP_MODE_FAN_ONLY,
|
||||||
|
AUTO_START_STOP_STOP_MODE_DRY,
|
||||||
|
AUTO_START_STOP_STOP_MODES,
|
||||||
overrides,
|
overrides,
|
||||||
)
|
)
|
||||||
from .commons import write_event_log
|
from .commons import write_event_log
|
||||||
@@ -44,14 +53,16 @@ async def async_setup_entry(
|
|||||||
_LOGGER.debug("%s - Calling async_setup_entry entry=%s, data=%s", name, entry.entry_id, entry.data)
|
_LOGGER.debug("%s - Calling async_setup_entry entry=%s, data=%s", name, entry.entry_id, entry.data)
|
||||||
vt_type = entry.data.get(CONF_THERMOSTAT_TYPE)
|
vt_type = entry.data.get(CONF_THERMOSTAT_TYPE)
|
||||||
|
|
||||||
if vt_type != CONF_THERMOSTAT_CENTRAL_CONFIG:
|
entities = []
|
||||||
return
|
|
||||||
|
|
||||||
entities = [
|
if vt_type == CONF_THERMOSTAT_CENTRAL_CONFIG:
|
||||||
CentralModeSelect(hass, unique_id, name, entry.data),
|
entities.append(CentralModeSelect(hass, unique_id, name, entry.data))
|
||||||
]
|
elif vt_type == CONF_THERMOSTAT_CLIMATE:
|
||||||
|
if entry.data.get(CONF_USE_AUTO_START_STOP_FEATURE) is True:
|
||||||
|
entities.append(AutoStartStopStopModeSelect(hass, unique_id, name, entry.data))
|
||||||
|
|
||||||
async_add_entities(entities, True)
|
if entities:
|
||||||
|
async_add_entities(entities, True)
|
||||||
|
|
||||||
|
|
||||||
class CentralModeSelect(SelectEntity, RestoreEntity):
|
class CentralModeSelect(SelectEntity, RestoreEntity):
|
||||||
@@ -124,3 +135,111 @@ class CentralModeSelect(SelectEntity, RestoreEntity):
|
|||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"VersatileThermostat-{self.name}"
|
return f"VersatileThermostat-{self.name}"
|
||||||
|
|
||||||
|
|
||||||
|
class AutoStartStopStopModeSelect(
|
||||||
|
VersatileThermostatBaseEntity, SelectEntity, RestoreEntity
|
||||||
|
):
|
||||||
|
"""Representation of the hvac_mode applied when the auto-start/stop
|
||||||
|
feature detects a stop condition (off, fan_only or dry)."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, hass: HomeAssistant, unique_id: str, name: str, entry_infos: ConfigData
|
||||||
|
):
|
||||||
|
"""Initialize the auto-start/stop stop mode select"""
|
||||||
|
super().__init__(hass, unique_id, name)
|
||||||
|
self._attr_name = "Auto start/stop stop mode"
|
||||||
|
self._attr_unique_id = f"{self._device_name}_auto_start_stop_stop_mode"
|
||||||
|
self._attr_translation_key = "auto_start_stop_stop_mode"
|
||||||
|
self._attr_entity_category = EntityCategory.CONFIG
|
||||||
|
self._attr_current_option = AUTO_START_STOP_STOP_MODE_OFF
|
||||||
|
|
||||||
|
@property
|
||||||
|
def icon(self) -> str | None:
|
||||||
|
"""The icon"""
|
||||||
|
return "mdi:hvac"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def options(self) -> list[str]:
|
||||||
|
"""The available options, computed from the underlying supported hvac_modes"""
|
||||||
|
return self._build_options()
|
||||||
|
|
||||||
|
def _build_options(self) -> list[str]:
|
||||||
|
"""Build the available options from the underlying supported hvac_modes.
|
||||||
|
fan_only and dry are only proposed if the underlying supports them."""
|
||||||
|
options = [AUTO_START_STOP_STOP_MODE_OFF]
|
||||||
|
climate = self.my_climate
|
||||||
|
if climate is not None:
|
||||||
|
hvac_modes = climate.hvac_modes
|
||||||
|
if AUTO_START_STOP_STOP_MODE_FAN_ONLY in hvac_modes:
|
||||||
|
options.append(AUTO_START_STOP_STOP_MODE_FAN_ONLY)
|
||||||
|
if AUTO_START_STOP_STOP_MODE_DRY in hvac_modes:
|
||||||
|
options.append(AUTO_START_STOP_STOP_MODE_DRY)
|
||||||
|
return options
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def my_climate_is_initialized(self):
|
||||||
|
"""Called when the associated climate is resolved -> refresh the options.
|
||||||
|
The restored option is only validated once the VTherm is fully
|
||||||
|
initialized (its underlying hvac_modes are available)."""
|
||||||
|
|
||||||
|
self._refresh_current_option()
|
||||||
|
self.hass.create_task(self.update_my_state_and_vtherm())
|
||||||
|
|
||||||
|
def _refresh_current_option(self):
|
||||||
|
"""Reset the current option to off only once the VTherm is fully
|
||||||
|
initialized and its underlying does not support the current option.
|
||||||
|
While the VTherm is not initialized, the restored option is kept as is
|
||||||
|
to avoid discarding it before the underlying hvac_modes are available."""
|
||||||
|
climate = self.my_climate
|
||||||
|
if climate is None or not climate.is_initialized:
|
||||||
|
return
|
||||||
|
if self._attr_current_option not in self.options:
|
||||||
|
self._attr_current_option = AUTO_START_STOP_STOP_MODE_OFF
|
||||||
|
|
||||||
|
@overrides
|
||||||
|
async def async_my_climate_changed(self, event: Event = None):
|
||||||
|
"""Called when my climate changes -> refresh the available options.
|
||||||
|
The underlying supported hvac_modes may become available only after
|
||||||
|
the VTherm has adopted them, so the options must be recomputed."""
|
||||||
|
if self.my_climate is None:
|
||||||
|
return
|
||||||
|
self._refresh_current_option()
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
@overrides
|
||||||
|
async def async_added_to_hass(self):
|
||||||
|
# Restore the persisted value before looking for the climate so that
|
||||||
|
# my_climate_is_initialized validates the options against it.
|
||||||
|
last_state = await self.async_get_last_state()
|
||||||
|
if last_state is not None and last_state.state in AUTO_START_STOP_STOP_MODES:
|
||||||
|
self._attr_current_option = last_state.state
|
||||||
|
|
||||||
|
await super().async_added_to_hass()
|
||||||
|
|
||||||
|
await self.update_my_state_and_vtherm()
|
||||||
|
|
||||||
|
async def update_my_state_and_vtherm(self):
|
||||||
|
"""Update the stop mode in my VTherm auto-start/stop manager"""
|
||||||
|
self.async_write_ha_state()
|
||||||
|
if (
|
||||||
|
self.my_climate is not None
|
||||||
|
and self.my_climate.auto_start_stop_manager is not None
|
||||||
|
):
|
||||||
|
await self.my_climate.auto_start_stop_manager.set_auto_start_stop_stop_mode(self._attr_current_option)
|
||||||
|
|
||||||
|
@overrides
|
||||||
|
async def async_select_option(self, option: str) -> None:
|
||||||
|
"""Change the selected option."""
|
||||||
|
if option == self._attr_current_option:
|
||||||
|
return
|
||||||
|
|
||||||
|
if option in self.options:
|
||||||
|
write_event_log(_LOGGER, self, f"Auto start/stop stop mode is being changed from {self._attr_current_option} to {option}")
|
||||||
|
self._attr_current_option = option
|
||||||
|
await self.update_my_state_and_vtherm()
|
||||||
|
|
||||||
|
@overrides
|
||||||
|
def select_option(self, option: str) -> None:
|
||||||
|
"""Change the selected option"""
|
||||||
|
self.hass.create_task(self.async_select_option(option))
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ from .const import (
|
|||||||
HVAC_OFF_REASON_AUTO_START_STOP,
|
HVAC_OFF_REASON_AUTO_START_STOP,
|
||||||
HVAC_OFF_REASON_SLEEP_MODE,
|
HVAC_OFF_REASON_SLEEP_MODE,
|
||||||
HVAC_OFF_REASON_CENTRAL_MODE,
|
HVAC_OFF_REASON_CENTRAL_MODE,
|
||||||
|
AUTO_START_STOP_HVAC_MODE_REASONS,
|
||||||
CONF_WINDOW_ECO_TEMP,
|
CONF_WINDOW_ECO_TEMP,
|
||||||
CONF_WINDOW_FAN_ONLY,
|
CONF_WINDOW_FAN_ONLY,
|
||||||
CONF_WINDOW_FROST_TEMP,
|
CONF_WINDOW_FROST_TEMP,
|
||||||
@@ -112,13 +113,16 @@ class StateManager:
|
|||||||
if vtherm.last_central_mode == CENTRAL_MODE_STOPPED:
|
if vtherm.last_central_mode == CENTRAL_MODE_STOPPED:
|
||||||
self._current_state.set_hvac_mode(VThermHvacMode_OFF)
|
self._current_state.set_hvac_mode(VThermHvacMode_OFF)
|
||||||
vtherm.set_hvac_off_reason(HVAC_OFF_REASON_CENTRAL_MODE)
|
vtherm.set_hvac_off_reason(HVAC_OFF_REASON_CENTRAL_MODE)
|
||||||
|
vtherm.set_hvac_mode_reason(HVAC_OFF_REASON_CENTRAL_MODE)
|
||||||
|
|
||||||
elif vtherm.safety_manager.is_safety_detected and (vtherm.is_over_climate or vtherm.safety_manager.safety_default_on_percent <= 0.0):
|
elif vtherm.safety_manager.is_safety_detected and (vtherm.is_over_climate or vtherm.safety_manager.safety_default_on_percent <= 0.0):
|
||||||
self._current_state.set_hvac_mode(VThermHvacMode_OFF)
|
self._current_state.set_hvac_mode(VThermHvacMode_OFF)
|
||||||
vtherm.set_hvac_off_reason(HVAC_OFF_REASON_SAFETY)
|
vtherm.set_hvac_off_reason(HVAC_OFF_REASON_SAFETY)
|
||||||
|
vtherm.set_hvac_mode_reason(HVAC_OFF_REASON_SAFETY)
|
||||||
|
|
||||||
# then check if window is open
|
# then check if window is open
|
||||||
elif vtherm.window_manager.is_window_detected and self._requested_state.hvac_mode != VThermHvacMode_OFF:
|
elif vtherm.window_manager.is_window_detected and self._requested_state.hvac_mode != VThermHvacMode_OFF:
|
||||||
|
vtherm.set_hvac_mode_reason(HVAC_OFF_REASON_WINDOW_DETECTION)
|
||||||
if vtherm.window_manager.window_action == CONF_WINDOW_FAN_ONLY and VThermHvacMode_FAN_ONLY in vtherm.vtherm_hvac_modes:
|
if vtherm.window_manager.window_action == CONF_WINDOW_FAN_ONLY and VThermHvacMode_FAN_ONLY in vtherm.vtherm_hvac_modes:
|
||||||
self._current_state.set_hvac_mode(VThermHvacMode_FAN_ONLY)
|
self._current_state.set_hvac_mode(VThermHvacMode_FAN_ONLY)
|
||||||
elif vtherm.window_manager.window_action == CONF_WINDOW_TURN_OFF or (
|
elif vtherm.window_manager.window_action == CONF_WINDOW_TURN_OFF or (
|
||||||
@@ -128,10 +132,17 @@ class StateManager:
|
|||||||
vtherm.set_hvac_off_reason(HVAC_OFF_REASON_WINDOW_DETECTION)
|
vtherm.set_hvac_off_reason(HVAC_OFF_REASON_WINDOW_DETECTION)
|
||||||
|
|
||||||
elif vtherm.auto_start_stop_manager and vtherm.auto_start_stop_manager.is_auto_stop_detected and self._requested_state.hvac_mode != VThermHvacMode_OFF:
|
elif vtherm.auto_start_stop_manager and vtherm.auto_start_stop_manager.is_auto_stop_detected and self._requested_state.hvac_mode != VThermHvacMode_OFF:
|
||||||
self._current_state.set_hvac_mode(VThermHvacMode_OFF)
|
stop_mode = vtherm.auto_start_stop_manager.stop_mode
|
||||||
vtherm.set_hvac_off_reason(HVAC_OFF_REASON_AUTO_START_STOP)
|
if stop_mode != VThermHvacMode_OFF and stop_mode in vtherm.vtherm_hvac_modes:
|
||||||
|
self._current_state.set_hvac_mode(stop_mode)
|
||||||
|
vtherm.set_hvac_mode_reason(AUTO_START_STOP_HVAC_MODE_REASONS.get(str(stop_mode), HVAC_OFF_REASON_AUTO_START_STOP))
|
||||||
|
else:
|
||||||
|
self._current_state.set_hvac_mode(VThermHvacMode_OFF)
|
||||||
|
vtherm.set_hvac_off_reason(HVAC_OFF_REASON_AUTO_START_STOP)
|
||||||
|
vtherm.set_hvac_mode_reason(HVAC_OFF_REASON_AUTO_START_STOP)
|
||||||
|
|
||||||
elif vtherm.last_central_mode == CENTRAL_MODE_COOL_ONLY and self._requested_state.hvac_mode != VThermHvacMode_OFF:
|
elif vtherm.last_central_mode == CENTRAL_MODE_COOL_ONLY and self._requested_state.hvac_mode != VThermHvacMode_OFF:
|
||||||
|
vtherm.set_hvac_mode_reason(HVAC_OFF_REASON_CENTRAL_MODE)
|
||||||
if VThermHvacMode_COOL in vtherm.vtherm_hvac_modes:
|
if VThermHvacMode_COOL in vtherm.vtherm_hvac_modes:
|
||||||
self._current_state.set_hvac_mode(VThermHvacMode_COOL)
|
self._current_state.set_hvac_mode(VThermHvacMode_COOL)
|
||||||
else:
|
else:
|
||||||
@@ -139,6 +150,7 @@ class StateManager:
|
|||||||
self._current_state.set_hvac_mode(VThermHvacMode_OFF)
|
self._current_state.set_hvac_mode(VThermHvacMode_OFF)
|
||||||
|
|
||||||
elif vtherm.last_central_mode == CENTRAL_MODE_HEAT_ONLY and self._requested_state.hvac_mode != VThermHvacMode_OFF:
|
elif vtherm.last_central_mode == CENTRAL_MODE_HEAT_ONLY and self._requested_state.hvac_mode != VThermHvacMode_OFF:
|
||||||
|
vtherm.set_hvac_mode_reason(HVAC_OFF_REASON_CENTRAL_MODE)
|
||||||
if VThermHvacMode_HEAT in vtherm.vtherm_hvac_modes:
|
if VThermHvacMode_HEAT in vtherm.vtherm_hvac_modes:
|
||||||
self._current_state.set_hvac_mode(VThermHvacMode_HEAT)
|
self._current_state.set_hvac_mode(VThermHvacMode_HEAT)
|
||||||
else:
|
else:
|
||||||
@@ -146,6 +158,7 @@ class StateManager:
|
|||||||
self._current_state.set_hvac_mode(VThermHvacMode_OFF)
|
self._current_state.set_hvac_mode(VThermHvacMode_OFF)
|
||||||
|
|
||||||
elif vtherm.last_central_mode == CENTRAL_MODE_FROST_PROTECTION and self._requested_state.hvac_mode != VThermHvacMode_OFF:
|
elif vtherm.last_central_mode == CENTRAL_MODE_FROST_PROTECTION and self._requested_state.hvac_mode != VThermHvacMode_OFF:
|
||||||
|
vtherm.set_hvac_mode_reason(HVAC_OFF_REASON_CENTRAL_MODE)
|
||||||
preset_modes = vtherm.vtherm_preset_modes
|
preset_modes = vtherm.vtherm_preset_modes
|
||||||
if preset_modes is None or VThermPreset.FROST not in preset_modes or VThermHvacMode_HEAT not in vtherm.vtherm_hvac_modes:
|
if preset_modes is None or VThermPreset.FROST not in preset_modes or VThermHvacMode_HEAT not in vtherm.vtherm_hvac_modes:
|
||||||
self._current_state.set_hvac_mode(VThermHvacMode_OFF)
|
self._current_state.set_hvac_mode(VThermHvacMode_OFF)
|
||||||
@@ -157,7 +170,11 @@ class StateManager:
|
|||||||
else:
|
else:
|
||||||
if self._current_state.hvac_mode == VThermHvacMode_OFF and self._requested_state.hvac_mode == VThermHvacMode_OFF:
|
if self._current_state.hvac_mode == VThermHvacMode_OFF and self._requested_state.hvac_mode == VThermHvacMode_OFF:
|
||||||
_LOGGER.info("%s - already in OFF. Change the reason to MANUAL", vtherm)
|
_LOGGER.info("%s - already in OFF. Change the reason to MANUAL", vtherm)
|
||||||
vtherm.set_hvac_off_reason(HVAC_OFF_REASON_MANUAL if not vtherm.is_sleeping else HVAC_OFF_REASON_SLEEP_MODE)
|
reason = HVAC_OFF_REASON_MANUAL if not vtherm.is_sleeping else HVAC_OFF_REASON_SLEEP_MODE
|
||||||
|
vtherm.set_hvac_off_reason(reason)
|
||||||
|
vtherm.set_hvac_mode_reason(reason)
|
||||||
|
else:
|
||||||
|
vtherm.set_hvac_mode_reason(None)
|
||||||
|
|
||||||
self._current_state.set_hvac_mode(self._requested_state.hvac_mode)
|
self._current_state.set_hvac_mode(self._requested_state.hvac_mode)
|
||||||
|
|
||||||
@@ -166,6 +183,7 @@ class StateManager:
|
|||||||
vtherm.set_hvac_off_reason(None)
|
vtherm.set_hvac_off_reason(None)
|
||||||
elif self._current_state.hvac_mode == VThermHvacMode_SLEEP:
|
elif self._current_state.hvac_mode == VThermHvacMode_SLEEP:
|
||||||
vtherm.set_hvac_off_reason(HVAC_OFF_REASON_SLEEP_MODE)
|
vtherm.set_hvac_off_reason(HVAC_OFF_REASON_SLEEP_MODE)
|
||||||
|
vtherm.set_hvac_mode_reason(HVAC_OFF_REASON_SLEEP_MODE)
|
||||||
|
|
||||||
return self._current_state.is_hvac_mode_changed
|
return self._current_state.is_hvac_mode_changed
|
||||||
|
|
||||||
|
|||||||
@@ -933,6 +933,16 @@
|
|||||||
"boost_ac_away_temp": {
|
"boost_ac_away_temp": {
|
||||||
"name": "Boost ac away"
|
"name": "Boost ac away"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"select": {
|
||||||
|
"auto_start_stop_stop_mode": {
|
||||||
|
"name": "Auto start/stop stop mode",
|
||||||
|
"state": {
|
||||||
|
"off": "Off",
|
||||||
|
"fan_only": "Fan only",
|
||||||
|
"dry": "Dry"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"services": {
|
"services": {
|
||||||
|
|||||||
@@ -174,6 +174,29 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if self.vtherm_hvac_mode not in (VThermHvacMode_HEAT, VThermHvacMode_COOL):
|
||||||
|
_LOGGER.debug(
|
||||||
|
"%s - auto-regulation is disabled cause VTherm hvac_mode is %s (not heat nor cool). Sending the raw target temperature",
|
||||||
|
self,
|
||||||
|
self.vtherm_hvac_mode,
|
||||||
|
)
|
||||||
|
# Outside of heat/cool the device is active but must receive the original
|
||||||
|
# (non regulated) target temperature. Force the regulated temperature to the
|
||||||
|
# target temperature and send it to all underlyings only when it differs from
|
||||||
|
# the last sent value, to avoid resending the same setpoint on each cycle.
|
||||||
|
self._regulated_target_temp = self.target_temperature
|
||||||
|
for under in self._underlyings:
|
||||||
|
if under.last_sent_temperature == self.target_temperature:
|
||||||
|
continue
|
||||||
|
await under.set_temperature(
|
||||||
|
self.target_temperature,
|
||||||
|
self._attr_max_temp,
|
||||||
|
self._attr_min_temp,
|
||||||
|
)
|
||||||
|
# Reset the timer of last regulation change to avoid time delta too high
|
||||||
|
self._last_regulation_change = self.now
|
||||||
|
return
|
||||||
|
|
||||||
_LOGGER.info(
|
_LOGGER.info(
|
||||||
"%s - Calling ThermostatClimate._send_regulated_temperature force=%s",
|
"%s - Calling ThermostatClimate._send_regulated_temperature force=%s",
|
||||||
self,
|
self,
|
||||||
@@ -416,8 +439,10 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]):
|
|||||||
DOMAIN,
|
DOMAIN,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not self._regulation_algo:
|
if self._auto_regulation_mode == CONF_AUTO_REGULATION_NONE or not self._regulation_algo:
|
||||||
# A default empty algo (which does nothing)
|
# A default empty algo (which does nothing). It must also replace any
|
||||||
|
# previously active algo when switching to None at runtime, else the
|
||||||
|
# old regulator keeps sending regulated setpoints to the underlyings.
|
||||||
self._regulation_algo = PITemperatureRegulator(self.target_temperature, 0, 0, 0, 0, 0, True)
|
self._regulation_algo = PITemperatureRegulator(self.target_temperature, 0, 0, 0, 0, 0, True)
|
||||||
|
|
||||||
def choose_auto_fan_mode(self, auto_fan_mode: str):
|
def choose_auto_fan_mode(self, auto_fan_mode: str):
|
||||||
@@ -440,8 +465,8 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def determine_fan_mode_contains_speed(fan_modes: list[str]) -> bool:
|
def determine_fan_mode_contains_speed(fan_modes: list[str]) -> bool:
|
||||||
"""Determine if the fan_modes contains speed modes by searching for the keywords "low"/"1"."""
|
"""Determine if the fan_modes contains speed modes by searching for the keywords "low"/"1"/"one"/"speed_1"."""
|
||||||
for val in ["low", "1"]:
|
for val in ["low", "1", "one", "speed_1"]:
|
||||||
if find_fan_mode(fan_modes, val):
|
if find_fan_mode(fan_modes, val):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
@@ -453,6 +478,10 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]):
|
|||||||
index = speed_modes.index("low")
|
index = speed_modes.index("low")
|
||||||
elif "1" in speed_modes:
|
elif "1" in speed_modes:
|
||||||
index = speed_modes.index("1")
|
index = speed_modes.index("1")
|
||||||
|
elif "one" in speed_modes:
|
||||||
|
index = speed_modes.index("one")
|
||||||
|
elif "speed_1" in speed_modes:
|
||||||
|
index = speed_modes.index("speed_1")
|
||||||
|
|
||||||
if index > -1 and index >= len(speed_modes) / 2:
|
if index > -1 and index >= len(speed_modes) / 2:
|
||||||
speed_modes.reverse()
|
speed_modes.reverse()
|
||||||
|
|||||||
@@ -300,7 +300,8 @@
|
|||||||
"heater_cooling_time": "Auskühlzeit (min)",
|
"heater_cooling_time": "Auskühlzeit (min)",
|
||||||
"auto_tpi_heating_rate": "Aufheizgeschwindigkeit ({unit}/h)",
|
"auto_tpi_heating_rate": "Aufheizgeschwindigkeit ({unit}/h)",
|
||||||
"auto_tpi_aggressiveness": "Aggressivität",
|
"auto_tpi_aggressiveness": "Aggressivität",
|
||||||
"auto_tpi_enable_advanced_settings": "Erweiterte Einstellungen aktivieren"
|
"auto_tpi_enable_advanced_settings": "Erweiterte Einstellungen aktivieren",
|
||||||
|
"auto_tpi_continuous_kext": "Kontinuierliches Kext-lernen"
|
||||||
},
|
},
|
||||||
"data_description": {
|
"data_description": {
|
||||||
"auto_tpi_learning_type": "Wähle 'Erkundung' für den ersten Start (gewichteter Durchschnitt, Gewicht 1) oder 'Feinabstimmung' für kontinuierliche Anpassungen (EWMA, Alpha 0,08).",
|
"auto_tpi_learning_type": "Wähle 'Erkundung' für den ersten Start (gewichteter Durchschnitt, Gewicht 1) oder 'Feinabstimmung' für kontinuierliche Anpassungen (EWMA, Alpha 0,08).",
|
||||||
@@ -308,7 +309,8 @@
|
|||||||
"heater_cooling_time": "Abkühlzeit nach dem Ausschalten (Minuten)\n\n| Typ | Aufheizzeit | Abkühlzeit |\n| :--- | :--- | :--- |\n| Elektroheizkörper | 5 Min. | 7 Min. |\n| Wasserheizkörper | 15 Min. | 20 Min. |\n| Fußbodenheizung | 30 Min. | 45 Min. |",
|
"heater_cooling_time": "Abkühlzeit nach dem Ausschalten (Minuten)\n\n| Typ | Aufheizzeit | Abkühlzeit |\n| :--- | :--- | :--- |\n| Elektroheizkörper | 5 Min. | 7 Min. |\n| Wasserheizkörper | 15 Min. | 20 Min. |\n| Fußbodenheizung | 30 Min. | 45 Min. |",
|
||||||
"auto_tpi_heating_rate": "Temperaturanstiegskapazität des Heizkörpers ({unit} pro Stunde). Kann über den Aktionsdienst 'Kapazität kalibrieren' geschätzt werden.",
|
"auto_tpi_heating_rate": "Temperaturanstiegskapazität des Heizkörpers ({unit} pro Stunde). Kann über den Aktionsdienst 'Kapazität kalibrieren' geschätzt werden.",
|
||||||
"auto_tpi_aggressiveness": "Skalierungsfaktor für die gelernten Koeffizienten (50-100 %). Niedrigere Werte führen zu konservativeren Koeffizienten und verringern das Risiko einer Überschreitung des Sollwerts.",
|
"auto_tpi_aggressiveness": "Skalierungsfaktor für die gelernten Koeffizienten (50-100 %). Niedrigere Werte führen zu konservativeren Koeffizienten und verringern das Risiko einer Überschreitung des Sollwerts.",
|
||||||
"auto_tpi_enable_advanced_settings": "Aktivieren Sie diese Option, um die Feinabstimmung der Parameter (Alpha, Abklingen, Gewicht) vorzunehmen."
|
"auto_tpi_enable_advanced_settings": "Option anwählen, um die Feinabstimmung der Parameter (Alpha, Abklingen, Gewicht) vorzunehmen.",
|
||||||
|
"auto_tpi_continuous_kext": "Kontrollkästchen aktivieren, um das kontinuierliche Lernen des externen Koeffizienten (Kext) außerhalb von AutoTPI-Sitzungen zu ermöglichen."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"auto_tpi_avg_settings": {
|
"auto_tpi_avg_settings": {
|
||||||
@@ -326,11 +328,13 @@
|
|||||||
"description": "Parameter für die EWMA-Methode.\n\nEmpfehlungen :\n| Situation | Alpha (ema_alpha) | Abklingrate (ema_decay_rate) |\n| :--- | :--- | :--- |\n| Lernstart | 0.15 | 0.08 |\n| Lernabschluß | 0.08 | 0.12 |\n| Kontinuierliches Lernen | 0.05 | 0.02 |",
|
"description": "Parameter für die EWMA-Methode.\n\nEmpfehlungen :\n| Situation | Alpha (ema_alpha) | Abklingrate (ema_decay_rate) |\n| :--- | :--- | :--- |\n| Lernstart | 0.15 | 0.08 |\n| Lernabschluß | 0.08 | 0.12 |\n| Kontinuierliches Lernen | 0.05 | 0.02 |",
|
||||||
"data": {
|
"data": {
|
||||||
"auto_tpi_ema_alpha": "Alpha",
|
"auto_tpi_ema_alpha": "Alpha",
|
||||||
"auto_tpi_ema_decay_rate": "Rückgangsrate"
|
"auto_tpi_ema_decay_rate": "Rückgangsrate",
|
||||||
|
"auto_tpi_continuous_kext_alpha": "Continuous Kext Alpha"
|
||||||
},
|
},
|
||||||
"data_description": {
|
"data_description": {
|
||||||
"auto_tpi_ema_alpha": "Glättungsfaktor (0-1). Höher = schnellere Anpassung",
|
"auto_tpi_ema_alpha": "Glättungsfaktor (0-1). Höher = schnellere Anpassung",
|
||||||
"auto_tpi_ema_decay_rate": "Rate, mit der Alpha im Laufe der Zeit abnimmt (Stabilisierung)"
|
"auto_tpi_ema_decay_rate": "Rate, mit der Alpha im Laufe der Zeit abnimmt (Stabilisierung)",
|
||||||
|
"auto_tpi_continuous_kext_alpha": "Smoothing factor for continuous Kext learning (Alpha). Default 0.04 (approx 3-5 days adaptation)."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sync_device_internal_temp": {
|
"sync_device_internal_temp": {
|
||||||
@@ -698,7 +702,8 @@
|
|||||||
"heater_heating_time": "Aufheizzeit (min)",
|
"heater_heating_time": "Aufheizzeit (min)",
|
||||||
"heater_cooling_time": "Abkühlzeit (min)",
|
"heater_cooling_time": "Abkühlzeit (min)",
|
||||||
"auto_tpi_aggressiveness": "Aggressivität",
|
"auto_tpi_aggressiveness": "Aggressivität",
|
||||||
"auto_tpi_enable_advanced_settings": "Erweiterte Einstellungen aktivieren"
|
"auto_tpi_enable_advanced_settings": "Erweiterte Einstellungen aktivieren",
|
||||||
|
"auto_tpi_continuous_kext": "Kontinuierliches Kext-lernen"
|
||||||
},
|
},
|
||||||
"data_description": {
|
"data_description": {
|
||||||
"auto_tpi_learning_type": "Wähle 'Erkundung' für den ersten Start (gewichteter Durchschnitt, Gewicht 1) oder 'Feinabstimmung' für kontinuierliche Anpassungen (EWMA, Alpha 0,08).",
|
"auto_tpi_learning_type": "Wähle 'Erkundung' für den ersten Start (gewichteter Durchschnitt, Gewicht 1) oder 'Feinabstimmung' für kontinuierliche Anpassungen (EWMA, Alpha 0,08).",
|
||||||
@@ -706,7 +711,8 @@
|
|||||||
"heater_heating_time": "Zeit bis zum Erreichen der vollen Leistung (Minuten)",
|
"heater_heating_time": "Zeit bis zum Erreichen der vollen Leistung (Minuten)",
|
||||||
"heater_cooling_time": "Abkühlzeit nach dem Ausschalten (Minuten)\n\n| Typ | Aufheizzeit | Abkühlzeit |\n| :--- | :--- | :--- |\n| Elektroheizkörper | 5 Min. | 7 Min. |\n| Wasserheizkörper | 15 Min. | 20 Min. |\n| Fußbodenheizung | 30 Min. | 45 Min. |",
|
"heater_cooling_time": "Abkühlzeit nach dem Ausschalten (Minuten)\n\n| Typ | Aufheizzeit | Abkühlzeit |\n| :--- | :--- | :--- |\n| Elektroheizkörper | 5 Min. | 7 Min. |\n| Wasserheizkörper | 15 Min. | 20 Min. |\n| Fußbodenheizung | 30 Min. | 45 Min. |",
|
||||||
"auto_tpi_aggressiveness": "Skalierungsfaktor für die gelernten Koeffizienten (50–100 %). Niedrigere Werte führen zu konservativeren Koeffizienten und verringern das Risiko einer Überschreitung des Sollwerts.",
|
"auto_tpi_aggressiveness": "Skalierungsfaktor für die gelernten Koeffizienten (50–100 %). Niedrigere Werte führen zu konservativeren Koeffizienten und verringern das Risiko einer Überschreitung des Sollwerts.",
|
||||||
"auto_tpi_enable_advanced_settings": "Kontrollkästchen aktivieren, um die Parameter des ausgewählten Algorithmus zu ändern."
|
"auto_tpi_enable_advanced_settings": "Kontrollkästchen aktivieren, um die Parameter des ausgewählten Algorithmus zu ändern.",
|
||||||
|
"auto_tpi_continuous_kext": "Kontrollkästchen aktivieren, um das kontinuierliche Lernen des externen Koeffizienten (Kext) außerhalb von AutoTPI-Sitzungen zu ermöglichen."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"auto_tpi_avg_settings": {
|
"auto_tpi_avg_settings": {
|
||||||
@@ -724,11 +730,13 @@
|
|||||||
"description": "Parameter für die EWMA-Methode.\n\nEmpfehlungen:\n| Situation | Alpha (ema_alpha) | Abklingrate (ema_decay_rate) |\n| :--- | :--- | :--- |\n| Anfängliches Lernen | 0,15 | 0,08 |\n| Endgültiges Lernen | 0,08 | 0,12 |\n| Kontinuierliches Lernen | 0,05 | 0,02 |",
|
"description": "Parameter für die EWMA-Methode.\n\nEmpfehlungen:\n| Situation | Alpha (ema_alpha) | Abklingrate (ema_decay_rate) |\n| :--- | :--- | :--- |\n| Anfängliches Lernen | 0,15 | 0,08 |\n| Endgültiges Lernen | 0,08 | 0,12 |\n| Kontinuierliches Lernen | 0,05 | 0,02 |",
|
||||||
"data": {
|
"data": {
|
||||||
"auto_tpi_ema_alpha": "Alpha",
|
"auto_tpi_ema_alpha": "Alpha",
|
||||||
"auto_tpi_ema_decay_rate": "Rückgangsrate"
|
"auto_tpi_ema_decay_rate": "Rückgangsrate",
|
||||||
|
"auto_tpi_continuous_kext_alpha": "Kontinuierliches Kext Alpha"
|
||||||
},
|
},
|
||||||
"data_description": {
|
"data_description": {
|
||||||
"auto_tpi_ema_alpha": "Glättungsfaktor (0-1). Höher = schnellere Anpassung",
|
"auto_tpi_ema_alpha": "Glättungsfaktor (0-1). Höher = schnellere Anpassung",
|
||||||
"auto_tpi_ema_decay_rate": "Rate, mit der Alpha im Laufe der Zeit abnimmt (Stabilisierung)"
|
"auto_tpi_ema_decay_rate": "Rate, mit der Alpha im Laufe der Zeit abnimmt (Stabilisierung)",
|
||||||
|
"auto_tpi_continuous_kext_alpha": "Glättungsfaktor für kontinuierliches Kext-Lernen (Alpha). Standardwert 0,04 (Anpassungszeit von ca. 3–5 Tagen)."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"heating_failure_detection": {
|
"heating_failure_detection": {
|
||||||
@@ -776,7 +784,7 @@
|
|||||||
"options": {
|
"options": {
|
||||||
"thermostat_central_config": "Zentrale Konfiguration",
|
"thermostat_central_config": "Zentrale Konfiguration",
|
||||||
"thermostat_over_switch": "Thermostat an einem Schalter",
|
"thermostat_over_switch": "Thermostat an einem Schalter",
|
||||||
"thermostat_over_climate": "Thermostat an einem Thermostat",
|
"thermostat_over_climate": "Thermostat mittels Thermostat/Klimaanlage",
|
||||||
"thermostat_over_valve": "Thermostat an einem Ventil"
|
"thermostat_over_valve": "Thermostat an einem Ventil"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1013,6 +1021,24 @@
|
|||||||
"description": "Der optionale Sperrcode"
|
"description": "Der optionale Sperrcode"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"download_logs": {
|
||||||
|
"name": "Download logs",
|
||||||
|
"description": "Gefilterte Protokolle für eine VTherm-Entität erfassen und herunterladen.",
|
||||||
|
"fields": {
|
||||||
|
"log_level": {
|
||||||
|
"name": "Log level",
|
||||||
|
"description": "Mindestprotokollstufe für die Erfassung"
|
||||||
|
},
|
||||||
|
"period_start": {
|
||||||
|
"name": "Period start",
|
||||||
|
"description": "Erfassungszeitraum Start"
|
||||||
|
},
|
||||||
|
"period_end": {
|
||||||
|
"name": "Period end",
|
||||||
|
"description": "Erfassungszeitraum Ende"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"exceptions": {
|
"exceptions": {
|
||||||
|
|||||||
@@ -933,6 +933,16 @@
|
|||||||
"boost_ac_away_temp": {
|
"boost_ac_away_temp": {
|
||||||
"name": "Boost ac away"
|
"name": "Boost ac away"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"select": {
|
||||||
|
"auto_start_stop_stop_mode": {
|
||||||
|
"name": "Auto start/stop stop mode",
|
||||||
|
"state": {
|
||||||
|
"off": "Off",
|
||||||
|
"fan_only": "Fan only",
|
||||||
|
"dry": "Dry"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"services": {
|
"services": {
|
||||||
|
|||||||
@@ -935,6 +935,16 @@
|
|||||||
"boost_ac_away_temp": {
|
"boost_ac_away_temp": {
|
||||||
"name": "Boost clim abs"
|
"name": "Boost clim abs"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"select": {
|
||||||
|
"auto_start_stop_stop_mode": {
|
||||||
|
"name": "Mode d'arrêt auto start/stop",
|
||||||
|
"state": {
|
||||||
|
"off": "Arrêt",
|
||||||
|
"fan_only": "Ventilation seule",
|
||||||
|
"dry": "Déshumidification"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"services": {
|
"services": {
|
||||||
|
|||||||
@@ -860,8 +860,15 @@ class UnderlyingClimate(UnderlyingEntity):
|
|||||||
|
|
||||||
_LOGGER.info("%s - Set setpoint temperature to: %s", self, target_temp)
|
_LOGGER.info("%s - Set setpoint temperature to: %s", self, target_temp)
|
||||||
|
|
||||||
# Issue 807 add TARGET_TEMPERATURE only if in the features
|
# Issue 807 add TARGET_TEMPERATURE only if in the features.
|
||||||
if ClimateEntityFeature.TARGET_TEMPERATURE_RANGE in self.supported_features:
|
# Use a bitwise test (not the `in` operator): self.supported_features is read
|
||||||
|
# from the underlying entity's state attributes, where Home Assistant may store
|
||||||
|
# it as a plain int (e.g. for restored states). `EnumMember in <int>` raises
|
||||||
|
# `TypeError: argument of type 'int' is not a container or iterable`, whereas
|
||||||
|
# the bitwise `&` works for both a plain int and a ClimateEntityFeature IntFlag.
|
||||||
|
# This matches the bitwise pattern already used elsewhere in this class
|
||||||
|
# (fan_modes / swing_modes).
|
||||||
|
if self.supported_features & ClimateEntityFeature.TARGET_TEMPERATURE_RANGE:
|
||||||
data.update(
|
data.update(
|
||||||
{
|
{
|
||||||
"target_temp_high": target_temp,
|
"target_temp_high": target_temp,
|
||||||
@@ -869,7 +876,7 @@ class UnderlyingClimate(UnderlyingEntity):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if ClimateEntityFeature.TARGET_TEMPERATURE in self.supported_features:
|
if self.supported_features & ClimateEntityFeature.TARGET_TEMPERATURE:
|
||||||
data["temperature"] = target_temp
|
data["temperature"] = target_temp
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1,4 +1,4 @@
|
|||||||
export const T_VERSION = "4.01";
|
export const T_VERSION = "4.02";
|
||||||
|
|
||||||
export const LANG_NAMES = Object.freeze({
|
export const LANG_NAMES = Object.freeze({
|
||||||
en: "English", cs: "Čeština", da: "Dansk", de: "Deutsch", es: "Español",
|
en: "English", cs: "Čeština", da: "Dansk", de: "Deutsch", es: "Español",
|
||||||
@@ -480,6 +480,7 @@ export const I18N = Object.freeze({
|
|||||||
"End": "Bitiş",
|
"End": "Bitiş",
|
||||||
"End value": "Bitiş değeri",
|
"End value": "Bitiş değeri",
|
||||||
"Energy Date Sync": "Enerji Tarih Senkronu",
|
"Energy Date Sync": "Enerji Tarih Senkronu",
|
||||||
|
"Key": "Anahtar",
|
||||||
"Entities": "Varlıklar",
|
"Entities": "Varlıklar",
|
||||||
"Entity": "Varlık",
|
"Entity": "Varlık",
|
||||||
"Falling": "Düşen",
|
"Falling": "Düşen",
|
||||||
@@ -573,6 +574,8 @@ export const I18N = Object.freeze({
|
|||||||
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Çizginin altındaki dolgunun gücü: 0 (görünmez) ile 1 (tam dolu) arası. Varsayılan 0.4 için boş bırakın. Gradient açıkken bu üstteki opaklıktır, aşağı doğru sekizde birine solar; Gradient kapalıyken düz dolgu bunun yarısını kullanır.",
|
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Çizginin altındaki dolgunun gücü: 0 (görünmez) ile 1 (tam dolu) arası. Varsayılan 0.4 için boş bırakın. Gradient açıkken bu üstteki opaklıktır, aşağı doğru sekizde birine solar; Gradient kapalıyken düz dolgu bunun yarısını kullanır.",
|
||||||
"Overlay": "Katman",
|
"Overlay": "Katman",
|
||||||
"Overlays are hidden in sparkline mode": "Katmanlar sparkline modunda gizlidir",
|
"Overlays are hidden in sparkline mode": "Katmanlar sparkline modunda gizlidir",
|
||||||
|
"Layout": "Yerleşim",
|
||||||
|
"Sequential": "Ardışık",
|
||||||
"Padding": "İç Boşluk",
|
"Padding": "İç Boşluk",
|
||||||
"Percentage": "Yüzde",
|
"Percentage": "Yüzde",
|
||||||
"Percentages": "Yüzdeler",
|
"Percentages": "Yüzdeler",
|
||||||
@@ -1259,6 +1262,7 @@ export const I18N = Object.freeze({
|
|||||||
"End": "Ende",
|
"End": "Ende",
|
||||||
"End value": "Endwert",
|
"End value": "Endwert",
|
||||||
"Energy Date Sync": "Energie-Datumssynchronisierung",
|
"Energy Date Sync": "Energie-Datumssynchronisierung",
|
||||||
|
"Key": "Schlüssel",
|
||||||
"Entities": "Entitäten",
|
"Entities": "Entitäten",
|
||||||
"Entity": "Entität",
|
"Entity": "Entität",
|
||||||
"Falling": "Fallend",
|
"Falling": "Fallend",
|
||||||
@@ -1352,6 +1356,8 @@ export const I18N = Object.freeze({
|
|||||||
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Wie kräftig die Füllung unter der Linie ist, von 0 (unsichtbar) bis 1 (deckend). Leer lassen für den Standardwert 0,4. Mit Farbverlauf ist dies die Deckkraft oben, die nach unten auf ein Achtel davon ausblendet; ohne Farbverlauf nutzt die flache Füllung die Hälfte davon.",
|
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Wie kräftig die Füllung unter der Linie ist, von 0 (unsichtbar) bis 1 (deckend). Leer lassen für den Standardwert 0,4. Mit Farbverlauf ist dies die Deckkraft oben, die nach unten auf ein Achtel davon ausblendet; ohne Farbverlauf nutzt die flache Füllung die Hälfte davon.",
|
||||||
"Overlay": "Overlay",
|
"Overlay": "Overlay",
|
||||||
"Overlays are hidden in sparkline mode": "Overlays sind im Sparkline-Modus ausgeblendet",
|
"Overlays are hidden in sparkline mode": "Overlays sind im Sparkline-Modus ausgeblendet",
|
||||||
|
"Layout": "Layout",
|
||||||
|
"Sequential": "Sequenziell",
|
||||||
"Padding": "Innenabstand",
|
"Padding": "Innenabstand",
|
||||||
"Percentage": "Prozentsatz",
|
"Percentage": "Prozentsatz",
|
||||||
"Percentages": "Prozentwerte",
|
"Percentages": "Prozentwerte",
|
||||||
@@ -2063,6 +2069,7 @@ export const I18N = Object.freeze({
|
|||||||
"End": "Konec",
|
"End": "Konec",
|
||||||
"End value": "Koncová hodnota",
|
"End value": "Koncová hodnota",
|
||||||
"Energy Date Sync": "Synchronizace s daty Energie",
|
"Energy Date Sync": "Synchronizace s daty Energie",
|
||||||
|
"Key": "Klíč",
|
||||||
"Entities": "Entity",
|
"Entities": "Entity",
|
||||||
"Entity": "Entita",
|
"Entity": "Entita",
|
||||||
"Falling": "Klesající",
|
"Falling": "Klesající",
|
||||||
@@ -2156,6 +2163,8 @@ export const I18N = Object.freeze({
|
|||||||
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Jak silná je výplň pod čarou, od 0 (neviditelná) do 1 (plná). Ponechte prázdné pro výchozí 0,4. Se zapnutým přechodem je to krytí nahoře, které dolů slábne na osminu; s vypnutým přechodem používá plochá výplň polovinu této hodnoty.",
|
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Jak silná je výplň pod čarou, od 0 (neviditelná) do 1 (plná). Ponechte prázdné pro výchozí 0,4. Se zapnutým přechodem je to krytí nahoře, které dolů slábne na osminu; s vypnutým přechodem používá plochá výplň polovinu této hodnoty.",
|
||||||
"Overlay": "Překryv",
|
"Overlay": "Překryv",
|
||||||
"Overlays are hidden in sparkline mode": "Překryvy jsou v režimu sparkline skryté",
|
"Overlays are hidden in sparkline mode": "Překryvy jsou v režimu sparkline skryté",
|
||||||
|
"Layout": "Rozvržení",
|
||||||
|
"Sequential": "Sekvenční",
|
||||||
"Padding": "Vnitřní okraj",
|
"Padding": "Vnitřní okraj",
|
||||||
"Percentage": "Procento",
|
"Percentage": "Procento",
|
||||||
"Percentages": "Procenta",
|
"Percentages": "Procenta",
|
||||||
@@ -2867,6 +2876,7 @@ export const I18N = Object.freeze({
|
|||||||
"End": "Slut",
|
"End": "Slut",
|
||||||
"End value": "Slutværdi",
|
"End value": "Slutværdi",
|
||||||
"Energy Date Sync": "Synkronisering med Energi-datoer",
|
"Energy Date Sync": "Synkronisering med Energi-datoer",
|
||||||
|
"Key": "Nøgle",
|
||||||
"Entities": "Enheder",
|
"Entities": "Enheder",
|
||||||
"Entity": "Enhed",
|
"Entity": "Enhed",
|
||||||
"Falling": "Faldende",
|
"Falling": "Faldende",
|
||||||
@@ -2960,6 +2970,8 @@ export const I18N = Object.freeze({
|
|||||||
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Hvor kraftig udfyldningen under linjen er, fra 0 (usynlig) til 1 (helt dækkende). Lad feltet stå tomt for standardværdien 0,4. Med gradient er dette dækkraften i toppen, der falmer til en ottendedel nederst; uden gradient bruger den flade udfyldning halvdelen.",
|
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Hvor kraftig udfyldningen under linjen er, fra 0 (usynlig) til 1 (helt dækkende). Lad feltet stå tomt for standardværdien 0,4. Med gradient er dette dækkraften i toppen, der falmer til en ottendedel nederst; uden gradient bruger den flade udfyldning halvdelen.",
|
||||||
"Overlay": "Overlejring",
|
"Overlay": "Overlejring",
|
||||||
"Overlays are hidden in sparkline mode": "Overlejringer er skjult i sparkline-tilstand",
|
"Overlays are hidden in sparkline mode": "Overlejringer er skjult i sparkline-tilstand",
|
||||||
|
"Layout": "Layout",
|
||||||
|
"Sequential": "Sekventiel",
|
||||||
"Padding": "Indre margen",
|
"Padding": "Indre margen",
|
||||||
"Percentage": "Procent",
|
"Percentage": "Procent",
|
||||||
"Percentages": "Procentdele",
|
"Percentages": "Procentdele",
|
||||||
@@ -3671,6 +3683,7 @@ export const I18N = Object.freeze({
|
|||||||
"End": "Fin",
|
"End": "Fin",
|
||||||
"End value": "Valor final",
|
"End value": "Valor final",
|
||||||
"Energy Date Sync": "Sincronización con fechas de Energía",
|
"Energy Date Sync": "Sincronización con fechas de Energía",
|
||||||
|
"Key": "Clave",
|
||||||
"Entities": "Entidades",
|
"Entities": "Entidades",
|
||||||
"Entity": "Entidad",
|
"Entity": "Entidad",
|
||||||
"Falling": "Descendente",
|
"Falling": "Descendente",
|
||||||
@@ -3764,6 +3777,8 @@ export const I18N = Object.freeze({
|
|||||||
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Qué tan intenso es el relleno bajo la línea, de 0 (invisible) a 1 (sólido). Déjalo vacío para el valor predeterminado 0,4. Con degradado es la opacidad en la parte superior, que se desvanece hasta un octavo abajo; sin degradado el relleno plano usa la mitad.",
|
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Qué tan intenso es el relleno bajo la línea, de 0 (invisible) a 1 (sólido). Déjalo vacío para el valor predeterminado 0,4. Con degradado es la opacidad en la parte superior, que se desvanece hasta un octavo abajo; sin degradado el relleno plano usa la mitad.",
|
||||||
"Overlay": "Superposición",
|
"Overlay": "Superposición",
|
||||||
"Overlays are hidden in sparkline mode": "Las superposiciones están ocultas en modo sparkline",
|
"Overlays are hidden in sparkline mode": "Las superposiciones están ocultas en modo sparkline",
|
||||||
|
"Layout": "Disposición",
|
||||||
|
"Sequential": "Secuencial",
|
||||||
"Padding": "Relleno interior",
|
"Padding": "Relleno interior",
|
||||||
"Percentage": "Porcentaje",
|
"Percentage": "Porcentaje",
|
||||||
"Percentages": "Porcentajes",
|
"Percentages": "Porcentajes",
|
||||||
@@ -4475,6 +4490,7 @@ export const I18N = Object.freeze({
|
|||||||
"End": "Loppu",
|
"End": "Loppu",
|
||||||
"End value": "Loppuarvo",
|
"End value": "Loppuarvo",
|
||||||
"Energy Date Sync": "Synkronointi Energia-päivämääriin",
|
"Energy Date Sync": "Synkronointi Energia-päivämääriin",
|
||||||
|
"Key": "Avain",
|
||||||
"Entities": "Entiteetit",
|
"Entities": "Entiteetit",
|
||||||
"Entity": "Entiteetti",
|
"Entity": "Entiteetti",
|
||||||
"Falling": "Laskeva",
|
"Falling": "Laskeva",
|
||||||
@@ -4568,6 +4584,8 @@ export const I18N = Object.freeze({
|
|||||||
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Kuinka vahva viivan alapuolinen täyttö on, väliltä 0 (näkymätön) ja 1 (täysin peittävä). Jätä tyhjäksi oletusarvolle 0,4. Liukuvärin kanssa tämä on peittävyys ylhäällä, joka häipyy alas kahdeksasosaan; ilman liukuväriä tasainen täyttö käyttää tästä puolet.",
|
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Kuinka vahva viivan alapuolinen täyttö on, väliltä 0 (näkymätön) ja 1 (täysin peittävä). Jätä tyhjäksi oletusarvolle 0,4. Liukuvärin kanssa tämä on peittävyys ylhäällä, joka häipyy alas kahdeksasosaan; ilman liukuväriä tasainen täyttö käyttää tästä puolet.",
|
||||||
"Overlay": "Päällys",
|
"Overlay": "Päällys",
|
||||||
"Overlays are hidden in sparkline mode": "Päällykset on piilotettu sparkline-tilassa",
|
"Overlays are hidden in sparkline mode": "Päällykset on piilotettu sparkline-tilassa",
|
||||||
|
"Layout": "Asettelu",
|
||||||
|
"Sequential": "Peräkkäinen",
|
||||||
"Padding": "Sisäreunus",
|
"Padding": "Sisäreunus",
|
||||||
"Percentage": "Prosentti",
|
"Percentage": "Prosentti",
|
||||||
"Percentages": "Prosentit",
|
"Percentages": "Prosentit",
|
||||||
@@ -5279,6 +5297,7 @@ export const I18N = Object.freeze({
|
|||||||
"End": "Fin",
|
"End": "Fin",
|
||||||
"End value": "Valeur finale",
|
"End value": "Valeur finale",
|
||||||
"Energy Date Sync": "Synchronisation des dates Énergie",
|
"Energy Date Sync": "Synchronisation des dates Énergie",
|
||||||
|
"Key": "Clé",
|
||||||
"Entities": "Entités",
|
"Entities": "Entités",
|
||||||
"Entity": "Entité",
|
"Entity": "Entité",
|
||||||
"Falling": "En baisse",
|
"Falling": "En baisse",
|
||||||
@@ -5372,6 +5391,8 @@ export const I18N = Object.freeze({
|
|||||||
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Intensité du remplissage sous la courbe, de 0 (invisible) à 1 (opaque). Laissez vide pour la valeur par défaut 0,4. Avec le dégradé, c'est l'opacité en haut, qui s'estompe jusqu'au huitième en bas ; sans dégradé, le remplissage uni en utilise la moitié.",
|
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Intensité du remplissage sous la courbe, de 0 (invisible) à 1 (opaque). Laissez vide pour la valeur par défaut 0,4. Avec le dégradé, c'est l'opacité en haut, qui s'estompe jusqu'au huitième en bas ; sans dégradé, le remplissage uni en utilise la moitié.",
|
||||||
"Overlay": "Superposition",
|
"Overlay": "Superposition",
|
||||||
"Overlays are hidden in sparkline mode": "Les superpositions sont masquées en mode sparkline",
|
"Overlays are hidden in sparkline mode": "Les superpositions sont masquées en mode sparkline",
|
||||||
|
"Layout": "Disposition",
|
||||||
|
"Sequential": "Séquentiel",
|
||||||
"Padding": "Marge intérieure",
|
"Padding": "Marge intérieure",
|
||||||
"Percentage": "Pourcentage",
|
"Percentage": "Pourcentage",
|
||||||
"Percentages": "Pourcentages",
|
"Percentages": "Pourcentages",
|
||||||
@@ -6083,6 +6104,7 @@ export const I18N = Object.freeze({
|
|||||||
"End": "Fine",
|
"End": "Fine",
|
||||||
"End value": "Valore finale",
|
"End value": "Valore finale",
|
||||||
"Energy Date Sync": "Sincronizzazione date Energia",
|
"Energy Date Sync": "Sincronizzazione date Energia",
|
||||||
|
"Key": "Chiave",
|
||||||
"Entities": "Entità",
|
"Entities": "Entità",
|
||||||
"Entity": "Entità",
|
"Entity": "Entità",
|
||||||
"Falling": "In discesa",
|
"Falling": "In discesa",
|
||||||
@@ -6176,6 +6198,8 @@ export const I18N = Object.freeze({
|
|||||||
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Quanto è intenso il riempimento sotto la linea, da 0 (invisibile) a 1 (pieno). Lascia vuoto per il valore predefinito 0,4. Con il gradiente è l'opacità in alto, che sfuma fino a un ottavo in basso; senza gradiente il riempimento piatto ne usa la metà.",
|
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Quanto è intenso il riempimento sotto la linea, da 0 (invisibile) a 1 (pieno). Lascia vuoto per il valore predefinito 0,4. Con il gradiente è l'opacità in alto, che sfuma fino a un ottavo in basso; senza gradiente il riempimento piatto ne usa la metà.",
|
||||||
"Overlay": "Overlay",
|
"Overlay": "Overlay",
|
||||||
"Overlays are hidden in sparkline mode": "Gli overlay sono nascosti in modalità sparkline",
|
"Overlays are hidden in sparkline mode": "Gli overlay sono nascosti in modalità sparkline",
|
||||||
|
"Layout": "Layout",
|
||||||
|
"Sequential": "Sequenziale",
|
||||||
"Padding": "Spaziatura interna",
|
"Padding": "Spaziatura interna",
|
||||||
"Percentage": "Percentuale",
|
"Percentage": "Percentuale",
|
||||||
"Percentages": "Percentuali",
|
"Percentages": "Percentuali",
|
||||||
@@ -6887,6 +6911,7 @@ export const I18N = Object.freeze({
|
|||||||
"End": "Einde",
|
"End": "Einde",
|
||||||
"End value": "Eindwaarde",
|
"End value": "Eindwaarde",
|
||||||
"Energy Date Sync": "Synchronisatie met Energie-datums",
|
"Energy Date Sync": "Synchronisatie met Energie-datums",
|
||||||
|
"Key": "Sleutel",
|
||||||
"Entities": "Entiteiten",
|
"Entities": "Entiteiten",
|
||||||
"Entity": "Entiteit",
|
"Entity": "Entiteit",
|
||||||
"Falling": "Dalend",
|
"Falling": "Dalend",
|
||||||
@@ -6980,6 +7005,8 @@ export const I18N = Object.freeze({
|
|||||||
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Hoe sterk de vulling onder de lijn is, van 0 (onzichtbaar) tot 1 (dekkend). Laat leeg voor de standaardwaarde 0,4. Met gradiënt is dit de dekking bovenaan, die naar onderen vervaagt tot een achtste; zonder gradiënt gebruikt de vlakke vulling de helft.",
|
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Hoe sterk de vulling onder de lijn is, van 0 (onzichtbaar) tot 1 (dekkend). Laat leeg voor de standaardwaarde 0,4. Met gradiënt is dit de dekking bovenaan, die naar onderen vervaagt tot een achtste; zonder gradiënt gebruikt de vlakke vulling de helft.",
|
||||||
"Overlay": "Overlay",
|
"Overlay": "Overlay",
|
||||||
"Overlays are hidden in sparkline mode": "Overlays zijn verborgen in sparkline-modus",
|
"Overlays are hidden in sparkline mode": "Overlays zijn verborgen in sparkline-modus",
|
||||||
|
"Layout": "Indeling",
|
||||||
|
"Sequential": "Sequentieel",
|
||||||
"Padding": "Binnenmarge",
|
"Padding": "Binnenmarge",
|
||||||
"Percentage": "Percentage",
|
"Percentage": "Percentage",
|
||||||
"Percentages": "Percentages",
|
"Percentages": "Percentages",
|
||||||
@@ -7691,6 +7718,7 @@ export const I18N = Object.freeze({
|
|||||||
"End": "Slutt",
|
"End": "Slutt",
|
||||||
"End value": "Sluttverdi",
|
"End value": "Sluttverdi",
|
||||||
"Energy Date Sync": "Synkronisering med Energi-datoer",
|
"Energy Date Sync": "Synkronisering med Energi-datoer",
|
||||||
|
"Key": "Nøkkel",
|
||||||
"Entities": "Entiteter",
|
"Entities": "Entiteter",
|
||||||
"Entity": "Entitet",
|
"Entity": "Entitet",
|
||||||
"Falling": "Fallende",
|
"Falling": "Fallende",
|
||||||
@@ -7784,6 +7812,8 @@ export const I18N = Object.freeze({
|
|||||||
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Hvor kraftig fyllet under linjen er, fra 0 (usynlig) til 1 (dekkende). La feltet stå tomt for standardverdien 0,4. Med gradient er dette dekkevnen øverst, som toner ned til en åttedel nederst; uten gradient bruker det flate fyllet halvparten.",
|
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Hvor kraftig fyllet under linjen er, fra 0 (usynlig) til 1 (dekkende). La feltet stå tomt for standardverdien 0,4. Med gradient er dette dekkevnen øverst, som toner ned til en åttedel nederst; uten gradient bruker det flate fyllet halvparten.",
|
||||||
"Overlay": "Overlegg",
|
"Overlay": "Overlegg",
|
||||||
"Overlays are hidden in sparkline mode": "Overlegg er skjult i sparkline-modus",
|
"Overlays are hidden in sparkline mode": "Overlegg er skjult i sparkline-modus",
|
||||||
|
"Layout": "Oppsett",
|
||||||
|
"Sequential": "Sekvensiell",
|
||||||
"Padding": "Indre marg",
|
"Padding": "Indre marg",
|
||||||
"Percentage": "Prosent",
|
"Percentage": "Prosent",
|
||||||
"Percentages": "Prosentandeler",
|
"Percentages": "Prosentandeler",
|
||||||
@@ -8495,6 +8525,7 @@ export const I18N = Object.freeze({
|
|||||||
"End": "Koniec",
|
"End": "Koniec",
|
||||||
"End value": "Wartość końcowa",
|
"End value": "Wartość końcowa",
|
||||||
"Energy Date Sync": "Synchronizacja z datami Energii",
|
"Energy Date Sync": "Synchronizacja z datami Energii",
|
||||||
|
"Key": "Klucz",
|
||||||
"Entities": "Encje",
|
"Entities": "Encje",
|
||||||
"Entity": "Encja",
|
"Entity": "Encja",
|
||||||
"Falling": "Spadek",
|
"Falling": "Spadek",
|
||||||
@@ -8588,6 +8619,8 @@ export const I18N = Object.freeze({
|
|||||||
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Jak mocne jest wypełnienie pod linią, od 0 (niewidoczne) do 1 (pełne). Pozostaw puste, aby użyć domyślnej wartości 0,4. Przy gradiencie jest to krycie u góry, zanikające do jednej ósmej na dole; bez gradientu płaskie wypełnienie używa połowy tej wartości.",
|
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Jak mocne jest wypełnienie pod linią, od 0 (niewidoczne) do 1 (pełne). Pozostaw puste, aby użyć domyślnej wartości 0,4. Przy gradiencie jest to krycie u góry, zanikające do jednej ósmej na dole; bez gradientu płaskie wypełnienie używa połowy tej wartości.",
|
||||||
"Overlay": "Nakładka",
|
"Overlay": "Nakładka",
|
||||||
"Overlays are hidden in sparkline mode": "Nakładki są ukryte w trybie sparkline",
|
"Overlays are hidden in sparkline mode": "Nakładki są ukryte w trybie sparkline",
|
||||||
|
"Layout": "Układ",
|
||||||
|
"Sequential": "Sekwencyjny",
|
||||||
"Padding": "Margines wewnętrzny",
|
"Padding": "Margines wewnętrzny",
|
||||||
"Percentage": "Procent",
|
"Percentage": "Procent",
|
||||||
"Percentages": "Procenty",
|
"Percentages": "Procenty",
|
||||||
@@ -9299,6 +9332,7 @@ export const I18N = Object.freeze({
|
|||||||
"End": "Fim",
|
"End": "Fim",
|
||||||
"End value": "Valor final",
|
"End value": "Valor final",
|
||||||
"Energy Date Sync": "Sincronização com datas de Energia",
|
"Energy Date Sync": "Sincronização com datas de Energia",
|
||||||
|
"Key": "Chave",
|
||||||
"Entities": "Entidades",
|
"Entities": "Entidades",
|
||||||
"Entity": "Entidade",
|
"Entity": "Entidade",
|
||||||
"Falling": "Em queda",
|
"Falling": "Em queda",
|
||||||
@@ -9392,6 +9426,8 @@ export const I18N = Object.freeze({
|
|||||||
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Qual a intensidade do preenchimento abaixo da linha, de 0 (invisível) a 1 (sólido). Deixe vazio para o padrão 0,4. Com gradiente esta é a opacidade no topo, esbatendo até um oitavo na base; sem gradiente o preenchimento liso usa metade.",
|
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Qual a intensidade do preenchimento abaixo da linha, de 0 (invisível) a 1 (sólido). Deixe vazio para o padrão 0,4. Com gradiente esta é a opacidade no topo, esbatendo até um oitavo na base; sem gradiente o preenchimento liso usa metade.",
|
||||||
"Overlay": "Sobreposição",
|
"Overlay": "Sobreposição",
|
||||||
"Overlays are hidden in sparkline mode": "As sobreposições ficam ocultas no modo sparkline",
|
"Overlays are hidden in sparkline mode": "As sobreposições ficam ocultas no modo sparkline",
|
||||||
|
"Layout": "Disposição",
|
||||||
|
"Sequential": "Sequencial",
|
||||||
"Padding": "Espaçamento interno",
|
"Padding": "Espaçamento interno",
|
||||||
"Percentage": "Porcentagem",
|
"Percentage": "Porcentagem",
|
||||||
"Percentages": "Porcentagens",
|
"Percentages": "Porcentagens",
|
||||||
@@ -10103,6 +10139,7 @@ export const I18N = Object.freeze({
|
|||||||
"End": "Конец",
|
"End": "Конец",
|
||||||
"End value": "Конечное значение",
|
"End value": "Конечное значение",
|
||||||
"Energy Date Sync": "Синхронизация с датами Энергии",
|
"Energy Date Sync": "Синхронизация с датами Энергии",
|
||||||
|
"Key": "Ключ",
|
||||||
"Entities": "Объекты",
|
"Entities": "Объекты",
|
||||||
"Entity": "Объект",
|
"Entity": "Объект",
|
||||||
"Falling": "Падение",
|
"Falling": "Падение",
|
||||||
@@ -10196,6 +10233,8 @@ export const I18N = Object.freeze({
|
|||||||
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Насколько плотная заливка под линией: от 0 (невидимая) до 1 (сплошная). Оставьте пустым для значения по умолчанию 0,4. С градиентом это непрозрачность вверху, затухающая до одной восьмой внизу; без градиента сплошная заливка использует половину.",
|
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Насколько плотная заливка под линией: от 0 (невидимая) до 1 (сплошная). Оставьте пустым для значения по умолчанию 0,4. С градиентом это непрозрачность вверху, затухающая до одной восьмой внизу; без градиента сплошная заливка использует половину.",
|
||||||
"Overlay": "Оверлей",
|
"Overlay": "Оверлей",
|
||||||
"Overlays are hidden in sparkline mode": "Оверлеи скрыты в режиме спарклайна",
|
"Overlays are hidden in sparkline mode": "Оверлеи скрыты в режиме спарклайна",
|
||||||
|
"Layout": "Компоновка",
|
||||||
|
"Sequential": "Последовательно",
|
||||||
"Padding": "Внутренний отступ",
|
"Padding": "Внутренний отступ",
|
||||||
"Percentage": "Процент",
|
"Percentage": "Процент",
|
||||||
"Percentages": "Проценты",
|
"Percentages": "Проценты",
|
||||||
@@ -10907,6 +10946,7 @@ export const I18N = Object.freeze({
|
|||||||
"End": "Slut",
|
"End": "Slut",
|
||||||
"End value": "Slutvärde",
|
"End value": "Slutvärde",
|
||||||
"Energy Date Sync": "Synkronisering med Energi-datum",
|
"Energy Date Sync": "Synkronisering med Energi-datum",
|
||||||
|
"Key": "Nyckel",
|
||||||
"Entities": "Entiteter",
|
"Entities": "Entiteter",
|
||||||
"Entity": "Entitet",
|
"Entity": "Entitet",
|
||||||
"Falling": "Fallande",
|
"Falling": "Fallande",
|
||||||
@@ -11000,6 +11040,8 @@ export const I18N = Object.freeze({
|
|||||||
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Hur kraftig fyllningen under linjen är, från 0 (osynlig) till 1 (heltäckande). Lämna tomt för standardvärdet 0,4. Med gradient är detta opaciteten upptill, som tonar ned till en åttondel nertill; utan gradient använder den platta fyllningen hälften.",
|
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "Hur kraftig fyllningen under linjen är, från 0 (osynlig) till 1 (heltäckande). Lämna tomt för standardvärdet 0,4. Med gradient är detta opaciteten upptill, som tonar ned till en åttondel nertill; utan gradient använder den platta fyllningen hälften.",
|
||||||
"Overlay": "Överlägg",
|
"Overlay": "Överlägg",
|
||||||
"Overlays are hidden in sparkline mode": "Överlägg är dolda i sparkline-läge",
|
"Overlays are hidden in sparkline mode": "Överlägg är dolda i sparkline-läge",
|
||||||
|
"Layout": "Layout",
|
||||||
|
"Sequential": "Sekventiell",
|
||||||
"Padding": "Inre marginal",
|
"Padding": "Inre marginal",
|
||||||
"Percentage": "Procent",
|
"Percentage": "Procent",
|
||||||
"Percentages": "Procentandelar",
|
"Percentages": "Procentandelar",
|
||||||
@@ -11711,6 +11753,7 @@ export const I18N = Object.freeze({
|
|||||||
"End": "结束",
|
"End": "结束",
|
||||||
"End value": "结束值",
|
"End value": "结束值",
|
||||||
"Energy Date Sync": "能源日期同步",
|
"Energy Date Sync": "能源日期同步",
|
||||||
|
"Key": "键",
|
||||||
"Entities": "实体",
|
"Entities": "实体",
|
||||||
"Entity": "实体",
|
"Entity": "实体",
|
||||||
"Falling": "下降",
|
"Falling": "下降",
|
||||||
@@ -11804,6 +11847,8 @@ export const I18N = Object.freeze({
|
|||||||
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "折线下方填充的强度,从 0(不可见)到 1(完全不透明)。留空则使用默认值 0.4。启用渐变时,这是顶部的不透明度,向下渐隐至其八分之一;关闭渐变时,纯色填充使用其一半。",
|
"How strong the fill under the line is, from 0 (invisible) to 1 (solid). Leave empty for the default 0.4. With Gradient on this is the opacity at the top, fading down to an eighth of it at the bottom; with Gradient off the flat fill uses half of it.": "折线下方填充的强度,从 0(不可见)到 1(完全不透明)。留空则使用默认值 0.4。启用渐变时,这是顶部的不透明度,向下渐隐至其八分之一;关闭渐变时,纯色填充使用其一半。",
|
||||||
"Overlay": "叠加层",
|
"Overlay": "叠加层",
|
||||||
"Overlays are hidden in sparkline mode": "迷你图模式下叠加层已隐藏",
|
"Overlays are hidden in sparkline mode": "迷你图模式下叠加层已隐藏",
|
||||||
|
"Layout": "布局",
|
||||||
|
"Sequential": "顺序",
|
||||||
"Padding": "内边距",
|
"Padding": "内边距",
|
||||||
"Percentage": "百分比",
|
"Percentage": "百分比",
|
||||||
"Percentages": "百分比",
|
"Percentages": "百分比",
|
||||||
|
|||||||
Binary file not shown.
+28
-28
@@ -4,10 +4,10 @@
|
|||||||
"state": "ON",
|
"state": "ON",
|
||||||
"led_brightness": 100,
|
"led_brightness": 100,
|
||||||
"countdown_to_turn_off": 0,
|
"countdown_to_turn_off": 0,
|
||||||
"voltage": 120.9,
|
"voltage": 121.3,
|
||||||
"countdown_to_turn_on": 0,
|
"countdown_to_turn_on": 0,
|
||||||
"ac_frequency": 60,
|
"ac_frequency": 60,
|
||||||
"power_factor": 0.11,
|
"power_factor": 0.14,
|
||||||
"update": {
|
"update": {
|
||||||
"state": "idle",
|
"state": "idle",
|
||||||
"installed_version": 268513381,
|
"installed_version": 268513381,
|
||||||
@@ -22,15 +22,15 @@
|
|||||||
},
|
},
|
||||||
"0xffffb40e0607af27": {
|
"0xffffb40e0607af27": {
|
||||||
"state": "ON",
|
"state": "ON",
|
||||||
"voltage": 120.5,
|
"voltage": 121.6,
|
||||||
"ac_frequency": 60,
|
"ac_frequency": 60,
|
||||||
"led_brightness": 100,
|
"led_brightness": 100,
|
||||||
"countdown_to_turn_off": 0,
|
"countdown_to_turn_off": 0,
|
||||||
"countdown_to_turn_on": 0,
|
"countdown_to_turn_on": 0,
|
||||||
"power": 2,
|
"power": 2.4,
|
||||||
"current": 0.12,
|
"current": 0.12,
|
||||||
"energy": 28.31,
|
"energy": 28.32,
|
||||||
"power_factor": 0.15,
|
"power_factor": 0.17,
|
||||||
"update": {
|
"update": {
|
||||||
"state": "idle",
|
"state": "idle",
|
||||||
"installed_version": 268513381,
|
"installed_version": 268513381,
|
||||||
@@ -45,10 +45,10 @@
|
|||||||
"state": "ON",
|
"state": "ON",
|
||||||
"led_brightness": 100,
|
"led_brightness": 100,
|
||||||
"countdown_to_turn_off": 0,
|
"countdown_to_turn_off": 0,
|
||||||
"voltage": 119.8,
|
"voltage": 121.1,
|
||||||
"countdown_to_turn_on": 0,
|
"countdown_to_turn_on": 0,
|
||||||
"energy": 54.49,
|
"energy": 54.57,
|
||||||
"power_factor": 0.99,
|
"power_factor": 0.2,
|
||||||
"ac_frequency": 60,
|
"ac_frequency": 60,
|
||||||
"update": {
|
"update": {
|
||||||
"state": "idle",
|
"state": "idle",
|
||||||
@@ -58,8 +58,8 @@
|
|||||||
"latest_release_notes": null
|
"latest_release_notes": null
|
||||||
},
|
},
|
||||||
"linkquality": 134,
|
"linkquality": 134,
|
||||||
"power": 200.4,
|
"power": 0.2,
|
||||||
"current": 1.66,
|
"current": 0.01,
|
||||||
"power_on_behavior": "on"
|
"power_on_behavior": "on"
|
||||||
},
|
},
|
||||||
"0xb40e060fffe031e3": {
|
"0xb40e060fffe031e3": {
|
||||||
@@ -74,12 +74,12 @@
|
|||||||
"led_brightness": 100,
|
"led_brightness": 100,
|
||||||
"countdown_to_turn_off": 0,
|
"countdown_to_turn_off": 0,
|
||||||
"countdown_to_turn_on": 0,
|
"countdown_to_turn_on": 0,
|
||||||
"voltage": 118.8,
|
"voltage": 119.7,
|
||||||
"state": "ON",
|
"state": "ON",
|
||||||
"ac_frequency": 60,
|
"ac_frequency": 60,
|
||||||
"energy": 112.09,
|
"energy": 112.17,
|
||||||
"power": 99.9,
|
"power": 94.6,
|
||||||
"current": 0.88,
|
"current": 0.84,
|
||||||
"power_factor": 0.94,
|
"power_factor": 0.94,
|
||||||
"update": {
|
"update": {
|
||||||
"state": "idle",
|
"state": "idle",
|
||||||
@@ -95,13 +95,13 @@
|
|||||||
"led_brightness": 100,
|
"led_brightness": 100,
|
||||||
"countdown_to_turn_off": 0,
|
"countdown_to_turn_off": 0,
|
||||||
"countdown_to_turn_on": 0,
|
"countdown_to_turn_on": 0,
|
||||||
"voltage": 120.5,
|
"voltage": 121.5,
|
||||||
"energy": 49.37,
|
"energy": 49.43,
|
||||||
"state": "ON",
|
"state": "ON",
|
||||||
"power": 42.7,
|
"power": 39.2,
|
||||||
"current": 0.47,
|
"current": 0.5,
|
||||||
"ac_frequency": 60,
|
"ac_frequency": 60,
|
||||||
"power_factor": 0.75,
|
"power_factor": 0.68,
|
||||||
"update": {
|
"update": {
|
||||||
"state": "idle",
|
"state": "idle",
|
||||||
"installed_version": 268513381,
|
"installed_version": 268513381,
|
||||||
@@ -114,12 +114,12 @@
|
|||||||
},
|
},
|
||||||
"0xffffb40e060895b3": {
|
"0xffffb40e060895b3": {
|
||||||
"state": "ON",
|
"state": "ON",
|
||||||
"voltage": 120.7,
|
"voltage": 121.2,
|
||||||
"ac_frequency": 60,
|
"ac_frequency": 60,
|
||||||
"energy": 7.14,
|
"energy": 7.14,
|
||||||
"current": 0.01,
|
"current": 0.01,
|
||||||
"power": 0.1,
|
"power": 0.1,
|
||||||
"power_factor": 0.13,
|
"power_factor": 0.11,
|
||||||
"linkquality": 123,
|
"linkquality": 123,
|
||||||
"update": {
|
"update": {
|
||||||
"state": "idle",
|
"state": "idle",
|
||||||
@@ -136,14 +136,14 @@
|
|||||||
"0xffffb40e0608864e": {
|
"0xffffb40e0608864e": {
|
||||||
"led_brightness": 100,
|
"led_brightness": 100,
|
||||||
"countdown_to_turn_off": 0,
|
"countdown_to_turn_off": 0,
|
||||||
"voltage": 120.5,
|
"voltage": 121.5,
|
||||||
"energy": 17.5,
|
"energy": 17.5,
|
||||||
"countdown_to_turn_on": 0,
|
"countdown_to_turn_on": 0,
|
||||||
"state": "ON",
|
"state": "ON",
|
||||||
"current": 0.02,
|
"current": 0.02,
|
||||||
"ac_frequency": 60,
|
"ac_frequency": 60,
|
||||||
"power": 0.3,
|
"power": 0.3,
|
||||||
"power_factor": 0.14,
|
"power_factor": 0.2,
|
||||||
"update": {
|
"update": {
|
||||||
"state": "idle",
|
"state": "idle",
|
||||||
"installed_version": 268513381,
|
"installed_version": 268513381,
|
||||||
@@ -172,13 +172,13 @@
|
|||||||
"0xffffb40e060893d8": {
|
"0xffffb40e060893d8": {
|
||||||
"state": "ON",
|
"state": "ON",
|
||||||
"led_brightness": 100,
|
"led_brightness": 100,
|
||||||
"voltage": 120.8,
|
"voltage": 121.9,
|
||||||
"countdown_to_turn_off": 0,
|
"countdown_to_turn_off": 0,
|
||||||
"countdown_to_turn_on": 0,
|
"countdown_to_turn_on": 0,
|
||||||
"energy": 3.1,
|
"energy": 3.1,
|
||||||
"power_on_behavior": "on",
|
"power_on_behavior": "on",
|
||||||
"linkquality": 87,
|
"linkquality": 87,
|
||||||
"current": 0.03,
|
"current": 0.01,
|
||||||
"ac_frequency": 60,
|
"ac_frequency": 60,
|
||||||
"update": {
|
"update": {
|
||||||
"state": "idle",
|
"state": "idle",
|
||||||
@@ -187,8 +187,8 @@
|
|||||||
"latest_source": "https://raw.githubusercontent.com/Koenkk/zigbee-OTA/master/images/ThirdReality/SmartPlug_Zigbee_PROD_OTA_V101_1.01.01.ota",
|
"latest_source": "https://raw.githubusercontent.com/Koenkk/zigbee-OTA/master/images/ThirdReality/SmartPlug_Zigbee_PROD_OTA_V101_1.01.01.ota",
|
||||||
"latest_release_notes": null
|
"latest_release_notes": null
|
||||||
},
|
},
|
||||||
"power_factor": 0.13,
|
"power_factor": 0,
|
||||||
"power": 0.1
|
"power": 0.2
|
||||||
},
|
},
|
||||||
"0xa4c1380d0679ffff": {
|
"0xa4c1380d0679ffff": {
|
||||||
"battery": 100,
|
"battery": 100,
|
||||||
|
|||||||
Reference in New Issue
Block a user