diff --git a/custom_components/versatile_thermostat/config_schema.py b/custom_components/versatile_thermostat/config_schema.py index 31a67a4..8f12b98 100644 --- a/custom_components/versatile_thermostat/config_schema.py +++ b/custom_components/versatile_thermostat/config_schema.py @@ -20,6 +20,10 @@ from homeassistant.components.select import ( DOMAIN as SELECT_DOMAIN, ) +from homeassistant.components.input_select import ( + DOMAIN as INPUT_SELECT_DOMAIN, +) + from homeassistant.components.input_datetime import ( DOMAIN as INPUT_DATETIME_DOMAIN, ) @@ -124,7 +128,7 @@ STEP_CENTRAL_BOILER_SCHEMA = vol.Schema( STEP_THERMOSTAT_SWITCH = vol.Schema( # pylint: disable=invalid-name { vol.Required(CONF_UNDERLYING_LIST): selector.EntitySelector( - selector.EntitySelectorConfig(domain=[SWITCH_DOMAIN, INPUT_BOOLEAN_DOMAIN, SELECT_DOMAIN, CLIMATE_DOMAIN], multiple=True), + selector.EntitySelectorConfig(domain=[SWITCH_DOMAIN, INPUT_BOOLEAN_DOMAIN, SELECT_DOMAIN, INPUT_SELECT_DOMAIN, CLIMATE_DOMAIN], multiple=True), ), vol.Optional(CONF_HEATER_KEEP_ALIVE): cv.positive_int, vol.Required(CONF_PROP_FUNCTION, default=PROPORTIONAL_FUNCTION_TPI): vol.In( diff --git a/custom_components/versatile_thermostat/underlyings.py b/custom_components/versatile_thermostat/underlyings.py index bd3f606..25d024c 100644 --- a/custom_components/versatile_thermostat/underlyings.py +++ b/custom_components/versatile_thermostat/underlyings.py @@ -2,7 +2,9 @@ """ Underlying entities classes """ import logging -from typing import Any +import re +from typing import Any, Dict, Tuple + from enum import StrEnum from homeassistant.const import ATTR_ENTITY_ID, STATE_ON, STATE_UNAVAILABLE @@ -228,6 +230,7 @@ class UnderlyingSwitch(UnderlyingEntity): self._keep_alive = IntervalCaller(hass, keep_alive_sec) self._vswitch_on = vswitch_on self._vswitch_off = vswitch_off + self._domain = self._entity_id.split(".")[0] @property def initial_delay_sec(self): @@ -298,18 +301,39 @@ class UnderlyingSwitch(UnderlyingEntity): ) await (self.turn_on() if self.is_device_active else self.turn_off()) - # @overrides this breaks some unit tests TypeError: object MagicMock can't be used in 'await' expression + def build_command(self, use_on: bool) -> Tuple[str, Dict[str, str]]: + """Build a command and returns a command and a dict as data""" + + data = {ATTR_ENTITY_ID: self._entity_id} + vswitch = self._vswitch_on if use_on and not self.is_inversed else self._vswitch_off + if vswitch: + pattern = r"^(?P[^/]+)(?:/(?P[^:]+)(?::(?P.*))?)?$" + match = re.match(pattern, vswitch) + + if match: + # Extraire les groupes nommés + command = match.group("command") + argument = match.group("argument") + value = match.group("value") + data.update({argument: value}) + else: + raise ValueError(f"Invalid input format: {vswitch}") + + else: + command = SERVICE_TURN_ON if use_on and not self.is_inversed else SERVICE_TURN_OFF + + return command, data + async def turn_off(self): """Turn heater toggleable device off.""" self._keep_alive.cancel() # Cancel early to avoid a turn_on/turn_off race condition _LOGGER.debug("%s - Stopping underlying entity %s", self, self._entity_id) - command = SERVICE_TURN_OFF if not self.is_inversed else SERVICE_TURN_ON - domain = self._entity_id.split(".")[0] + + command, data = self.build_command(use_on=False) # This may fails if called after shutdown try: try: - data = {ATTR_ENTITY_ID: self._entity_id} - await self._hass.services.async_call(domain, command, data) + await self._hass.services.async_call(self._domain, command, data) self._keep_alive.set_async_action(self._keep_alive_callback) except Exception: self._keep_alive.cancel() @@ -325,12 +349,10 @@ class UnderlyingSwitch(UnderlyingEntity): if not await self.check_overpowering(): return False - command = SERVICE_TURN_ON if not self.is_inversed else SERVICE_TURN_OFF - domain = self._entity_id.split(".")[0] + command, data = self.build_command(use_on=True) try: try: - data = {ATTR_ENTITY_ID: self._entity_id} - await self._hass.services.async_call(domain, command, data) + await self._hass.services.async_call(self._domain, command, data) self._keep_alive.set_async_action(self._keep_alive_callback) return True except Exception: