Compare commits

..

3 Commits
6.0.3 ... 6.1.0

Author SHA1 Message Date
Jean-Marc Collin
a9b87b3aee Release 6.1.0 2024-03-26 20:10:29 +00:00
Jean-Marc Collin
c512cb6f74 [#428] - Refacto start versatile_thermostat (#430)
Co-authored-by: Jean-Marc Collin <jean-marc.collin-extern@renault.com>
2024-03-26 21:06:25 +01:00
Jean-Marc Collin
9269240fe3 Release 2024-03-25 07:43:09 +00:00
9 changed files with 270 additions and 206 deletions

View File

@@ -3,9 +3,9 @@ default_config:
logger: logger:
default: warning default: warning
logs: logs:
# custom_components.versatile_thermostat: debug custom_components.versatile_thermostat: info
# custom_components.versatile_thermostat.underlyings: debug custom_components.versatile_thermostat.underlyings: info
# custom_components.versatile_thermostat.climate: debug custom_components.versatile_thermostat.climate: info
# If you need to debug uncommment the line below (doc: https://www.home-assistant.io/integrations/debugpy/) # If you need to debug uncommment the line below (doc: https://www.home-assistant.io/integrations/debugpy/)
debugpy: debugpy:

View File

@@ -107,6 +107,8 @@ async def async_setup(
"VersatileThermostat - HA is started, initialize all links between VTherm entities" "VersatileThermostat - HA is started, initialize all links between VTherm entities"
) )
await api.init_vtherm_links() await api.init_vtherm_links()
await api.notify_central_mode_change()
await api.reload_central_boiler_entities_list()
if hass.state == CoreState.running: if hass.state == CoreState.running:
await _async_startup_internal() await _async_startup_internal()
@@ -156,8 +158,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
await api.reload_central_boiler_entities_list() if hass.state == CoreState.running:
await api.init_vtherm_links() await api.reload_central_boiler_entities_list()
await api.init_vtherm_links()
return True return True

View File

@@ -629,7 +629,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
self.async_on_remove(self.remove_thermostat) self.async_on_remove(self.remove_thermostat)
await self.async_startup() # issue 428. Link to others entities will start at link
# await self.async_startup()
def remove_thermostat(self): def remove_thermostat(self):
"""Called when the thermostat will be removed""" """Called when the thermostat will be removed"""
@@ -637,155 +638,157 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
for under in self._underlyings: for under in self._underlyings:
under.remove_entity() under.remove_entity()
async def async_startup(self): async def async_startup(self, central_configuration):
"""Triggered on startup, used to get old state and set internal states accordingly""" """Triggered on startup, used to get old state and set internal states accordingly"""
_LOGGER.debug("%s - Calling async_startup", self) _LOGGER.debug("%s - Calling async_startup", self)
@callback _LOGGER.debug("%s - Calling async_startup_internal", self)
async def _async_startup_internal(*_): need_write_state = False
_LOGGER.debug("%s - Calling async_startup_internal", self)
need_write_state = False
# Initialize all UnderlyingEntities await self.get_my_previous_state()
self.init_underlyings()
temperature_state = self.hass.states.get(self._temp_sensor_entity_id) await self.init_presets(central_configuration)
if temperature_state and temperature_state.state not in (
# Initialize all UnderlyingEntities
self.init_underlyings()
temperature_state = self.hass.states.get(self._temp_sensor_entity_id)
if temperature_state and temperature_state.state not in (
STATE_UNAVAILABLE,
STATE_UNKNOWN,
):
_LOGGER.debug(
"%s - temperature sensor have been retrieved: %.1f",
self,
float(temperature_state.state),
)
await self._async_update_temp(temperature_state)
need_write_state = True
if self._ext_temp_sensor_entity_id:
ext_temperature_state = self.hass.states.get(
self._ext_temp_sensor_entity_id
)
if ext_temperature_state and ext_temperature_state.state not in (
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
STATE_UNKNOWN, STATE_UNKNOWN,
): ):
_LOGGER.debug( _LOGGER.debug(
"%s - temperature sensor have been retrieved: %.1f", "%s - external temperature sensor have been retrieved: %.1f",
self, self,
float(temperature_state.state), float(ext_temperature_state.state),
) )
await self._async_update_temp(temperature_state) await self._async_update_ext_temp(ext_temperature_state)
need_write_state = True
if self._ext_temp_sensor_entity_id:
ext_temperature_state = self.hass.states.get(
self._ext_temp_sensor_entity_id
)
if ext_temperature_state and ext_temperature_state.state not in (
STATE_UNAVAILABLE,
STATE_UNKNOWN,
):
_LOGGER.debug(
"%s - external temperature sensor have been retrieved: %.1f",
self,
float(ext_temperature_state.state),
)
await self._async_update_ext_temp(ext_temperature_state)
else:
_LOGGER.debug(
"%s - external temperature sensor have NOT been retrieved cause unknown or unavailable",
self,
)
else: else:
_LOGGER.debug( _LOGGER.debug(
"%s - external temperature sensor have NOT been retrieved cause no external sensor", "%s - external temperature sensor have NOT been retrieved cause unknown or unavailable",
self, self,
) )
if self._pmax_on:
# try to acquire current power and power max
current_power_state = self.hass.states.get(self._power_sensor_entity_id)
if current_power_state and current_power_state.state not in (
STATE_UNAVAILABLE,
STATE_UNKNOWN,
):
self._current_power = float(current_power_state.state)
_LOGGER.debug(
"%s - Current power have been retrieved: %.3f",
self,
self._current_power,
)
need_write_state = True
# Try to acquire power max
current_power_max_state = self.hass.states.get(
self._max_power_sensor_entity_id
)
if current_power_max_state and current_power_max_state.state not in (
STATE_UNAVAILABLE,
STATE_UNKNOWN,
):
self._current_power_max = float(current_power_max_state.state)
_LOGGER.debug(
"%s - Current power max have been retrieved: %.3f",
self,
self._current_power_max,
)
need_write_state = True
# try to acquire window entity state
if self._window_sensor_entity_id:
window_state = self.hass.states.get(self._window_sensor_entity_id)
if window_state and window_state.state not in (
STATE_UNAVAILABLE,
STATE_UNKNOWN,
):
self._window_state = window_state.state == STATE_ON
_LOGGER.debug(
"%s - Window state have been retrieved: %s",
self,
self._window_state,
)
need_write_state = True
# try to acquire motion entity state
if self._motion_sensor_entity_id:
motion_state = self.hass.states.get(self._motion_sensor_entity_id)
if motion_state and motion_state.state not in (
STATE_UNAVAILABLE,
STATE_UNKNOWN,
):
self._motion_state = motion_state.state
_LOGGER.debug(
"%s - Motion state have been retrieved: %s",
self,
self._motion_state,
)
# recalculate the right target_temp in activity mode
await self._async_update_motion_temp()
need_write_state = True
if self._presence_on:
# try to acquire presence entity state
presence_state = self.hass.states.get(self._presence_sensor_entity_id)
if presence_state and presence_state.state not in (
STATE_UNAVAILABLE,
STATE_UNKNOWN,
):
await self._async_update_presence(presence_state.state)
_LOGGER.debug(
"%s - Presence have been retrieved: %s",
self,
presence_state.state,
)
need_write_state = True
if need_write_state:
self.async_write_ha_state()
if self._prop_algorithm:
self._prop_algorithm.calculate(
self._target_temp,
self._cur_temp,
self._cur_ext_temp,
self._hvac_mode or HVACMode.OFF,
)
self.reset_last_change_time()
await self.get_my_previous_state()
if self.hass.state == CoreState.running:
await _async_startup_internal()
else: else:
self.hass.bus.async_listen_once( _LOGGER.debug(
EVENT_HOMEASSISTANT_START, _async_startup_internal "%s - external temperature sensor have NOT been retrieved cause no external sensor",
self,
) )
if self._pmax_on:
# try to acquire current power and power max
current_power_state = self.hass.states.get(self._power_sensor_entity_id)
if current_power_state and current_power_state.state not in (
STATE_UNAVAILABLE,
STATE_UNKNOWN,
):
self._current_power = float(current_power_state.state)
_LOGGER.debug(
"%s - Current power have been retrieved: %.3f",
self,
self._current_power,
)
need_write_state = True
# Try to acquire power max
current_power_max_state = self.hass.states.get(
self._max_power_sensor_entity_id
)
if current_power_max_state and current_power_max_state.state not in (
STATE_UNAVAILABLE,
STATE_UNKNOWN,
):
self._current_power_max = float(current_power_max_state.state)
_LOGGER.debug(
"%s - Current power max have been retrieved: %.3f",
self,
self._current_power_max,
)
need_write_state = True
# try to acquire window entity state
if self._window_sensor_entity_id:
window_state = self.hass.states.get(self._window_sensor_entity_id)
if window_state and window_state.state not in (
STATE_UNAVAILABLE,
STATE_UNKNOWN,
):
self._window_state = window_state.state == STATE_ON
_LOGGER.debug(
"%s - Window state have been retrieved: %s",
self,
self._window_state,
)
need_write_state = True
# try to acquire motion entity state
if self._motion_sensor_entity_id:
motion_state = self.hass.states.get(self._motion_sensor_entity_id)
if motion_state and motion_state.state not in (
STATE_UNAVAILABLE,
STATE_UNKNOWN,
):
self._motion_state = motion_state.state
_LOGGER.debug(
"%s - Motion state have been retrieved: %s",
self,
self._motion_state,
)
# recalculate the right target_temp in activity mode
await self._async_update_motion_temp()
need_write_state = True
if self._presence_on:
# try to acquire presence entity state
presence_state = self.hass.states.get(self._presence_sensor_entity_id)
if presence_state and presence_state.state not in (
STATE_UNAVAILABLE,
STATE_UNKNOWN,
):
await self._async_update_presence(presence_state.state)
_LOGGER.debug(
"%s - Presence have been retrieved: %s",
self,
presence_state.state,
)
need_write_state = True
if need_write_state:
self.async_write_ha_state()
if self._prop_algorithm:
self._prop_algorithm.calculate(
self._target_temp,
self._cur_temp,
self._cur_ext_temp,
self._hvac_mode or HVACMode.OFF,
)
self.hass.create_task(self._check_initial_state())
self.reset_last_change_time()
# if self.hass.state == CoreState.running:
# await _async_startup_internal()
# else:
# self.hass.bus.async_listen_once(
# EVENT_HOMEASSISTANT_START, _async_startup_internal
# )
def init_underlyings(self): def init_underlyings(self):
"""Initialize all underlyings. Should be overriden if necessary""" """Initialize all underlyings. Should be overriden if necessary"""
@@ -825,19 +828,20 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
# Never restore a Power or Security preset # Never restore a Power or Security preset
if old_preset_mode is not None and old_preset_mode not in HIDDEN_PRESETS: if old_preset_mode is not None and old_preset_mode not in HIDDEN_PRESETS:
# old_preset_mode in self._attr_preset_modes # old_preset_mode in self._attr_preset_modes
self._attr_preset_mode = old_state.attributes.get(ATTR_PRESET_MODE) self._attr_preset_mode = old_preset_mode
self.save_preset_mode() self.save_preset_mode()
else: else:
self._attr_preset_mode = PRESET_NONE self._attr_preset_mode = PRESET_NONE
if not self._hvac_mode and old_state.state in [ if old_state.state in [
HVACMode.OFF, HVACMode.OFF,
HVACMode.HEAT, HVACMode.HEAT,
HVACMode.COOL, HVACMode.COOL,
]: ]:
self._hvac_mode = old_state.state self._hvac_mode = old_state.state
else: else:
self._hvac_mode = HVACMode.OFF if not self._hvac_mode:
self._hvac_mode = HVACMode.OFF
old_total_energy = old_state.attributes.get(ATTR_TOTAL_ENERGY) old_total_energy = old_state.attributes.get(ATTR_TOTAL_ENERGY)
if old_total_energy: if old_total_energy:
@@ -2085,6 +2089,8 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
new_central_mode, new_central_mode,
) )
first_init = self._last_central_mode == None
self._last_central_mode = new_central_mode self._last_central_mode = new_central_mode
def save_all(): def save_all():
@@ -2093,7 +2099,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
self.save_hvac_mode() self.save_hvac_mode()
if new_central_mode == CENTRAL_MODE_AUTO: if new_central_mode == CENTRAL_MODE_AUTO:
if self.window_state is not STATE_ON: if self.window_state is not STATE_ON and not first_init:
await self.restore_hvac_mode() await self.restore_hvac_mode()
await self.restore_preset_mode() await self.restore_preset_mode()
@@ -2707,8 +2713,6 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]):
if self._attr_preset_mode: if self._attr_preset_mode:
await self._async_set_preset_mode_internal(self._attr_preset_mode, True) await self._async_set_preset_mode_internal(self._attr_preset_mode, True)
self.hass.create_task(self._check_initial_state())
async def async_turn_off(self) -> None: async def async_turn_off(self) -> None:
await self.async_set_hvac_mode(HVACMode.OFF) await self.async_set_hvac_mode(HVACMode.OFF)

View File

@@ -14,6 +14,6 @@
"quality_scale": "silver", "quality_scale": "silver",
"requirements": [], "requirements": [],
"ssdp": [], "ssdp": [],
"version": "6.0.2", "version": "6.1.0",
"zeroconf": [] "zeroconf": []
} }

View File

@@ -353,7 +353,7 @@ class CentralConfigTemperatureNumber(
# We have to reload all VTherm for which uses the central configuration # We have to reload all VTherm for which uses the central configuration
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(self.hass) api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(self.hass)
# Update the VTherms which have temperature in central config # Update the VTherms which have temperature in central config
self.hass.create_task(api.init_vtherm_links(only_use_central=True)) self.hass.create_task(api.init_vtherm_preset_with_central())
def __str__(self): def __str__(self):
return f"VersatileThermostat-{self.name}" return f"VersatileThermostat-{self.name}"

View File

@@ -18,6 +18,9 @@ from custom_components.versatile_thermostat.base_thermostat import (
BaseThermostat, BaseThermostat,
ConfigData, ConfigData,
) )
from custom_components.versatile_thermostat.vtherm_api import VersatileThermostatAPI
from .const import ( from .const import (
DOMAIN, DOMAIN,
DEVICE_MANUFACTURER, DEVICE_MANUFACTURER,
@@ -96,17 +99,20 @@ class CentralModeSelect(SelectEntity, RestoreEntity):
if old_state is not None: if old_state is not None:
self._attr_current_option = old_state.state self._attr_current_option = old_state.state
@callback api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(self.hass)
async def _async_startup_internal(*_): api.register_central_mode_select(self)
_LOGGER.debug("%s - Calling async_startup_internal", self)
await self.notify_central_mode_change()
if self.hass.state == CoreState.running: # @callback
await _async_startup_internal() # async def _async_startup_internal(*_):
else: # _LOGGER.debug("%s - Calling async_startup_internal", self)
self.hass.bus.async_listen_once( # await self.notify_central_mode_change()
EVENT_HOMEASSISTANT_START, _async_startup_internal #
) # if self.hass.state == CoreState.running:
# await _async_startup_internal()
# else:
# self.hass.bus.async_listen_once(
# EVENT_HOMEASSISTANT_START, _async_startup_internal
# )
@overrides @overrides
async def async_select_option(self, option: str) -> None: async def async_select_option(self, option: str) -> None:
@@ -122,17 +128,9 @@ class CentralModeSelect(SelectEntity, RestoreEntity):
async def notify_central_mode_change(self, old_central_mode: str | None = None): async def notify_central_mode_change(self, old_central_mode: str | None = None):
"""Notify all VTherm that the central_mode have change""" """Notify all VTherm that the central_mode have change"""
api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(self.hass)
# Update all VTherm states # Update all VTherm states
component: EntityComponent[ClimateEntity] = self.hass.data[CLIMATE_DOMAIN] await api.notify_central_mode_change(old_central_mode)
for entity in component.entities:
if isinstance(entity, BaseThermostat):
_LOGGER.debug(
"Changing the central_mode. We have find %s to update",
entity.name,
)
await entity.check_central_mode(
self._attr_current_option, old_central_mode
)
def __str__(self) -> str: def __str__(self) -> str:
return f"VersatileThermostat-{self.name}" return f"VersatileThermostat-{self.name}"

View File

@@ -486,8 +486,8 @@ class UnderlyingClimate(UnderlyingEntity):
self._underlying_climate, self._underlying_climate,
) )
else: else:
_LOGGER.error( _LOGGER.info(
"%s - Cannot find the underlying climate entity: %s. Thermostat will not be operational", "%s - Cannot find the underlying climate entity: %s. Thermostat will not be operational. Will try later.",
self, self,
self.entity_id, self.entity_id,
) )
@@ -780,15 +780,19 @@ class UnderlyingValve(UnderlyingEntity):
"""Send the percent open to the underlying valve""" """Send the percent open to the underlying valve"""
# This may fails if called after shutdown # This may fails if called after shutdown
try: try:
data = {ATTR_ENTITY_ID: self._entity_id, "value": self._percent_open} data = {"value": self._percent_open}
target = {ATTR_ENTITY_ID: self._entity_id}
domain = self._entity_id.split(".")[0] domain = self._entity_id.split(".")[0]
await self._hass.services.async_call( await self._hass.services.async_call(
domain, domain=domain,
SERVICE_SET_VALUE, service=SERVICE_SET_VALUE,
data, service_data=data,
target=target,
) )
except ServiceNotFound as err: except ServiceNotFound as err:
_LOGGER.error(err) _LOGGER.error(err)
# This could happens in unit test if input_number domain is not yet loaded
# raise err
async def turn_off(self): async def turn_off(self):
"""Turn heater toggleable device off.""" """Turn heater toggleable device off."""

View File

@@ -57,6 +57,7 @@ class VersatileThermostatAPI(dict):
self._threshold_number_entity = None self._threshold_number_entity = None
self._nb_active_number_entity = None self._nb_active_number_entity = None
self._central_configuration = None self._central_configuration = None
self._central_mode_select = None
# A dict that will store all Number entities which holds the temperature # A dict that will store all Number entities which holds the temperature
self._number_temperatures = dict() self._number_temperatures = dict()
@@ -149,8 +150,8 @@ class VersatileThermostatAPI(dict):
return entity.state return entity.state
return None return None
async def init_vtherm_links(self, only_use_central=False): async def init_vtherm_links(self):
"""INitialize all VTherms entities links """Initialize all VTherms entities links
This method is called when HA is fully started (and all entities should be initialized) This method is called when HA is fully started (and all entities should be initialized)
Or when we need to reload all VTherm links (with Number temp entities, central boiler, ...) Or when we need to reload all VTherm links (with Number temp entities, central boiler, ...)
""" """
@@ -162,12 +163,34 @@ class VersatileThermostatAPI(dict):
) )
if component: if component:
for entity in component.entities: for entity in component.entities:
if hasattr(entity, "init_presets"): # if hasattr(entity, "init_presets"):
if ( # if (
only_use_central is False # only_use_central is False
or entity.use_central_config_temperature # or entity.use_central_config_temperature
): # ):
await entity.init_presets(self.find_central_configuration()) # await entity.init_presets(self.find_central_configuration())
# A little hack to test if the climate is a VTherm. Cannot use isinstance due to circular dependency of BaseThermostat
if (
entity.device_info
and entity.device_info.get("model", None) == DOMAIN
):
await entity.async_startup(self.find_central_configuration())
async def init_vtherm_preset_with_central(self):
"""Init all VTherm presets when the VTherm uses central temperature"""
# Initialization of all preset for all VTherm
component: EntityComponent[ClimateEntity] = self._hass.data.get(
CLIMATE_DOMAIN, None
)
if component:
for entity in component.entities:
if (
entity.device_info
and entity.device_info.get("model", None) == DOMAIN
and entity.use_central_config_temperature
):
await entity.init_presets(self.find_central_configuration())
async def reload_central_boiler_binary_listener(self): async def reload_central_boiler_binary_listener(self):
"""Reloads the BinarySensor entity which listen to the number of """Reloads the BinarySensor entity which listen to the number of
@@ -180,6 +203,27 @@ class VersatileThermostatAPI(dict):
if self._nb_active_number_entity is not None: if self._nb_active_number_entity is not None:
await self._nb_active_number_entity.listen_vtherms_entities() await self._nb_active_number_entity.listen_vtherms_entities()
def register_central_mode_select(self, central_mode_select):
"""Register the select entity which holds the central_mode"""
self._central_mode_select = central_mode_select
async def notify_central_mode_change(self, old_central_mode: str | None = None):
"""Notify all VTherm that the central_mode have change"""
if self._central_mode_select is None:
return
# Update all VTherm states
component: EntityComponent[ClimateEntity] = self.hass.data[CLIMATE_DOMAIN]
for entity in component.entities:
if entity.device_info and entity.device_info.get("model", None) == DOMAIN:
_LOGGER.debug(
"Changing the central_mode. We have find %s to update",
entity.name,
)
await entity.check_central_mode(
self._central_mode_select.state, old_central_mode
)
@property @property
def self_regulation_expert(self): def self_regulation_expert(self):
"""Get the self regulation params""" """Get the self regulation params"""
@@ -229,6 +273,14 @@ class VersatileThermostatAPI(dict):
return None return None
return int(self._threshold_number_entity.native_value) return int(self._threshold_number_entity.native_value)
@property
def central_mode(self) -> str | None:
"""Get the current central mode or None"""
if self._central_mode_select:
return self._central_mode_select.state
else:
return None
@property @property
def hass(self): def hass(self):
"""Get the HomeAssistant object""" """Get the HomeAssistant object"""

View File

@@ -181,14 +181,18 @@ async def test_over_valve_full_start(
mock_service_call.assert_has_calls( mock_service_call.assert_has_calls(
[ [
call.async_call( call.async_call(
"number", domain="number",
"set_value", service="set_value",
{"entity_id": "number.mock_valve", "value": 90}, service_data={"value": 90},
target={"entity_id": "number.mock_valve"},
# {"entity_id": "number.mock_valve", "value": 90},
), ),
call.async_call( call.async_call(
"number", domain="number",
"set_value", service="set_value",
{"entity_id": "number.mock_valve", "value": 98}, service_data={"value": 98},
target={"entity_id": "number.mock_valve"},
# {"entity_id": "number.mock_valve", "value": 98},
), ),
] ]
) )
@@ -241,9 +245,10 @@ async def test_over_valve_full_start(
mock_service_call.assert_has_calls( mock_service_call.assert_has_calls(
[ [
call.async_call( call.async_call(
"number", domain="number",
"set_value", service="set_value",
{"entity_id": "number.mock_valve", "value": 10}, service_data={"value": 10},
target={"entity_id": "number.mock_valve"},
) )
] ]
) )
@@ -254,20 +259,16 @@ async def test_over_valve_full_start(
mock_service_call.assert_has_calls( mock_service_call.assert_has_calls(
[ [
call.async_call( call.async_call(
"number", domain="number",
"set_value", service="set_value",
{ service_data={"value": 10},
"entity_id": "number.mock_valve", target={"entity_id": "number.mock_valve"}, # the min allowed value
"value": 10,
}, # the min allowed value
), ),
call.async_call( call.async_call(
"number", domain="number",
"set_value", service="set_value",
{ service_data={"value": 50}, # the min allowed value
"entity_id": "number.mock_valve", target={"entity_id": "number.mock_valve"},
"value": 50,
}, # the max allowed value
), ),
] ]
) )
@@ -466,9 +467,10 @@ async def test_over_valve_regulation(
mock_service_call.assert_has_calls( mock_service_call.assert_has_calls(
[ [
call.async_call( call.async_call(
"number", domain="number",
"set_value", service="set_value",
{"entity_id": "number.mock_valve", "value": 90}, service_data={"value": 90},
target={"entity_id": "number.mock_valve"},
), ),
] ]
) )
@@ -524,9 +526,10 @@ async def test_over_valve_regulation(
mock_service_call.assert_has_calls( mock_service_call.assert_has_calls(
[ [
call.async_call( call.async_call(
"number", domain="number",
"set_value", service="set_value",
{"entity_id": "number.mock_valve", "value": 96}, service_data={"value": 96},
target={"entity_id": "number.mock_valve"},
), ),
] ]
) )