diff --git a/controller/actor.py b/controller/actor.py index 9c62174..6758050 100644 --- a/controller/actor.py +++ b/controller/actor.py @@ -56,10 +56,12 @@ def __init__(self, **kwargs): super().__init__(auto_transitions=False, ignore_invalid_triggers=True, **kwargs) self.__state_time = None - def __update_state_time(self): + def __update_state_time(self, *args, **kwargs): self.__state_time = datetime.now() def get_time_in_state(self): + if self.__state_time is None: + return datetime.now() - datetime.now() return datetime.now() - self.__state_time @@ -76,13 +78,6 @@ def on_failure(self, exception_type, exception_value, traceback): def on_stop(self): self.do_cancel() - def get_actor(self, name): - fsm = pykka.ActorRegistry.get_by_class_name(name) - if fsm: - return fsm[0].proxy() - logger.critical(f"Actor {name} not found!!!") - return None - def __do_cancel(self): if self.__timer: self.__timer.cancel() diff --git a/controller/filtration.py b/controller/filtration.py index 35cfae6..14e14ef 100644 --- a/controller/filtration.py +++ b/controller/filtration.py @@ -190,6 +190,17 @@ def update(self, now): class Filtration(PoupoolActor): + def __notify_swim(self): + is_opened = self.is_overflow_normal() or self.is_standby_normal() or self.is_comfort() + is_wintering = self.is_wintering_waiting() or self.is_wintering_stir() + self.__swim.set_filtration_state(is_opened, is_wintering) + + def __check_tank_fault(self): + if self.__tank.is_fault(): + self._proxy.halt.defer() + return True + return False + STATE_REFRESH_DELAY = 10 HEATING_DELAY_TO_ECO = int(config["heating", "delay_to_eco"]) HEATING_DELAY_TO_OPEN = int(config["heating", "delay_to_open"]) @@ -217,11 +228,18 @@ class Filtration(PoupoolActor): {"name": "wintering", "initial": "waiting", "children": ["stir", "waiting"]}, ] - def __init__(self, temperature, encoder, devices): + def __init__(self, temperature, encoder, devices, tank, swim, heating, disinfection, arduino, heater, light): super().__init__() self.__temperature = temperature self.__encoder = encoder self.__devices = devices + self.__tank = tank + self.__swim = swim + self.__heating = heating + self.__disinfection = disinfection + self.__arduino = arduino + self.__heater = heater + self.__light = light # Parameters self.__eco_mode = EcoMode(encoder) self.__stir_mode = StirMode(devices) @@ -406,26 +424,22 @@ def backwash_last(self, value): logger.info(f"Backwash last set to: {self.__backwash_last}") def tank_start(self): - tank = self.get_actor("Tank") - if tank.is_halt().get(): - tank.fill.defer() + if self.__tank.is_halt().get(): + self.__tank.fill() # Call proxy and ignore future def heating_start(self): - heating = self.get_actor("Heating") - if heating.is_halt().get(): - heating.wait.defer() + if self.__heating.is_halt().get(): + self.__heating.wait() # Call proxy and ignore future def arduino_start(self): - arduino = self.get_actor("Arduino") - if arduino.is_halt().get(): - arduino.run.defer() + if self.__arduino.is_halt().get(): + self.__arduino.run() # Call proxy and ignore future def tank_is_low(self): - tank = self.get_actor("Tank") - return tank.is_halt().get() or tank.is_low().get() or tank.is_fill().get() + return self.__tank.is_halt().get() or self.__tank.is_low().get() or self.__tank.is_fill().get() def tank_is_high(self): - return self.get_actor("Tank").is_high().get() + return self.__tank.is_high().get() def pump_stopped_in_standby(self): return self.__speed_standby == 0 @@ -443,15 +457,13 @@ def __before_state_change(self): self.__eco_mode.clear() def __disinfection_start(self): - self.__actor_run("Disinfection") + self.__actor_run(self.__disinfection) - def __actor_run(self, name): - actor = self.get_actor(name) + def __actor_run(self, actor): if actor.is_halt().get(): actor.run.defer() - def __actor_halt(self, name): - actor = self.get_actor(name) + def __actor_halt(self, actor): try: # We have seen situation where this creates a deadlock. We add a timeout so that we # eventually return from the get() and force the halt transition. @@ -463,12 +475,12 @@ def __actor_halt(self, name): def on_enter_halt(self): logger.info("Entering halt state") self.__encoder.filtration_state("halt") - self.__actor_halt("Disinfection") + self.__actor_halt(self.__disinfection) self.__actor_halt("Tank") self.__actor_halt("Arduino") self.__actor_halt("Heating") - self.__actor_halt("Light") - self.__actor_halt("Swim") + self.__actor_halt(self.__light) + self.__actor_halt(self.__swim) self.__devices.get_pump("variable").off() self.__devices.get_pump("boost").off() self.__devices.get_valve("gravity").off() @@ -484,16 +496,18 @@ def on_exit_halt(self): def on_enter_closing(self): logger.info("Entering closing state") self.__encoder.filtration_state("closing") - self.__actor_halt("Disinfection") + self.__actor_halt(self.__disinfection) # stop the pumps to avoid perturbation in the water while shutter is moving self.__devices.get_valve("gravity").on() self.__devices.get_pump("boost").off() self.__devices.get_pump("variable").off() # close the roller shutter - self.get_actor("Arduino").cover_close.defer() + self.__arduino.cover_close.defer() def do_repeat_closing(self): - position = self.get_actor("Arduino").cover_position().get() + if self.__check_tank_fault(): + return + position = self.__arduino.cover_position().get() logger.debug(f"Cover position is {position}") self.__encoder.filtration_state(f"closing_{position // 10 * 10}") if position <= self.__cover_position_eco: @@ -509,7 +523,7 @@ def do_repeat_closing(self): def on_exit_closing(self): logger.info("Exiting closing state") # stop the roller shutter - self.get_actor("Arduino").cover_stop.defer() + self.__arduino.cover_stop.defer() # Clear the stir mode, we were opened before so we likely do not need to stir immediately self.__stir_mode.clear(None) @@ -517,16 +531,18 @@ def on_exit_closing(self): def on_enter_opening(self): logger.info("Entering opening state") self.__encoder.filtration_state("opening") - self.__actor_halt("Disinfection") + self.__actor_halt(self.__disinfection) # stop the pumps to avoid perturbation in the water while shutter is moving self.__devices.get_valve("gravity").on() self.__devices.get_pump("boost").off() self.__devices.get_pump("variable").off() # open the roller shutter - self.get_actor("Arduino").cover_open.defer() + self.__arduino.cover_open.defer() def do_repeat_opening(self): - position = self.get_actor("Arduino").cover_position().get() + if self.__check_tank_fault(): + return + position = self.__arduino.cover_position().get() logger.debug(f"Cover position is {position}") self.__encoder.filtration_state(f"opening_{position // 10 * 10}") if position == 100: @@ -539,17 +555,17 @@ def do_repeat_opening(self): def on_exit_opening(self): logger.info("Exiting opening state") # TODO I'm not quite sure why this is here!!!??? Needed? Bug? - self.get_actor("Tank").set_mode.defer("overflow") + self.__tank.set_mode.defer("overflow") # stop the roller shutter - self.get_actor("Arduino").cover_stop.defer() + self.__arduino.cover_stop.defer() def on_enter_eco(self): logger.info("Entering eco state") - self.__actor_halt("Light") + self.__actor_halt(self.__light) self.__devices.get_valve("drain").off() self.__devices.get_valve("gravity").on() self.__devices.get_valve("tank").off() - self.get_actor("Tank").set_mode.defer("eco") + self.__tank.set_mode.defer("eco") def on_enter_eco_compute(self): logger.info("Entering eco compute") @@ -572,26 +588,25 @@ def on_enter_eco_normal(self): self.__devices.get_pump("variable").speed(self.__speed_eco) def do_repeat_eco_normal(self): - now = datetime.now() - if self.__start_backwash(): - self._proxy.wash.defer() - elif self.__eco_mode.update(now): - self.__reload_eco() - elif self.__eco_mode.elapsed_on(): - if self.tank_is_low(): - self._proxy.eco_waiting.defer() - elif self.__eco_mode.tank_duration > timedelta(): - self._proxy.eco_tank.defer() - else: - self.__stir_mode.update(now) - self.do_delay(self.STATE_REFRESH_DELAY, self.do_repeat_eco_normal.__name__) + if self.__check_tank_fault(): + return + if self.tank_is_low(): + self._proxy.eco_tank.defer() + return + if self.__heating.wants_to_heat(): + self._proxy.heat.defer() + return + if self.__eco_mode.elapsed_on(): + self._proxy.eco_waiting.defer() + return + self.do_delay(self.STATE_REFRESH_DELAY, self.do_repeat_eco_normal.__name__) @do_repeat() def on_enter_eco_tank(self): logger.info("Entering eco_tank state") self.__encoder.filtration_state("eco_tank") self.__eco_mode.set_current(self.__eco_mode.tank_duration) - self.__actor_halt("Disinfection") + self.__actor_halt(self.__disinfection) self.__stir_mode.clear(datetime.now()) self.__devices.get_valve("tank").on() # We force the speed to 1 in tank mode because otherwise the tank will be emptied @@ -599,8 +614,12 @@ def on_enter_eco_tank(self): self.__devices.get_pump("variable").speed(1) def do_repeat_eco_tank(self): + if self.__check_tank_fault(): + return if self.__eco_mode.update(datetime.now()): self.__reload_eco() + elif self.__heating.wants_to_heat(): + self._proxy.heat.defer() elif self.__eco_mode.elapsed_on(): self._proxy.eco_waiting.defer() else: @@ -618,15 +637,19 @@ def on_enter_heating_running(self): self.__devices.get_pump("variable").speed(2) def do_repeat_heating_running(self): + if self.__check_tank_fault(): + return + if not self.__heating.is_heating().get(): + self._proxy.heating_delay.defer() + return now = datetime.now() self.__eco_mode.update(now) self.__stir_mode.update(now) self.do_delay(self.STATE_REFRESH_DELAY, self.do_repeat_heating_running.__name__) def on_exit_heating_running(self): - actor = self.get_actor("Heating") - if actor.is_heating().get(): - actor.wait.defer() + if self.__heating.is_heating().get(): + self.__heating.wait() # Call proxy and ignore future self.__stir_mode.clear(datetime.now()) def on_enter_heating_delay(self): @@ -649,13 +672,17 @@ def on_enter_eco_waiting(self): logger.info("Entering eco_waiting state") self.__encoder.filtration_state("eco_waiting") self.__eco_mode.set_current(self.__eco_mode.off_duration) - self.__actor_halt("Disinfection") + self.__actor_halt(self.__disinfection) self.__devices.get_pump("variable").off() def do_repeat_eco_waiting(self): + if self.__check_tank_fault(): + return now = datetime.now() if self.__start_backwash(): self._proxy.wash.defer() + elif self.__heating.wants_to_heat(): + self._proxy.heat.defer() elif self.__eco_mode.update(now, 0): self.__reload_eco() elif self.__eco_mode.elapsed_off(): @@ -672,7 +699,7 @@ def on_enter_standby(self): def on_enter_standby_boost(self): logger.info("Entering standby boost state") self.__encoder.filtration_state("standby_boost") - self.__actor_halt("Disinfection") + self.__actor_halt(self.__disinfection) self.__devices.get_valve("tank").on() self.__devices.get_pump("boost").on() self.__devices.get_pump("variable").speed(3) @@ -690,9 +717,11 @@ def on_enter_standby_normal(self): if self.__speed_standby > 0: self.__disinfection_start() else: - self.__actor_halt("Disinfection") + self.__actor_halt(self.__disinfection) def do_repeat_standby_normal(self): + if self.__check_tank_fault(): + return factor = 1 if self.__speed_standby > 0 else 0 self.__eco_mode.update(datetime.now(), factor) self.do_delay(self.STATE_REFRESH_DELAY, self.do_repeat_standby_normal.__name__) @@ -700,7 +729,7 @@ def do_repeat_standby_normal(self): def on_enter_sweep(self): logger.info("Entering sweep state") self.__encoder.filtration_state("sweep") - self.__actor_halt("Disinfection") + self.__actor_halt(self.__disinfection) self.__devices.get_valve("gravity").on() self.__devices.get_valve("tank").off() self.__devices.get_pump("variable").speed(3) @@ -727,15 +756,16 @@ def reload_comfort(self): valve.off() def do_repeat_comfort(self): + if self.__check_tank_fault(): + return self.__eco_mode.update(datetime.now(), 0.5) - actor = self.get_actor("Heating") - if not actor.is_forcing().get() and not actor.is_recovering().get(): - actor.force.defer() + if not self.__heating.is_forcing().get() and not self.__heating.is_recovering().get(): + self.__heating.force() # Call proxy and ignore future self.do_delay(self.STATE_REFRESH_DELAY, self.do_repeat_comfort.__name__) def on_exit_comfort(self): logger.info("Exiting comfort state") - self.get_actor("Heating").wait.defer() + self.__heating.wait() # Call proxy and ignore future def on_enter_overflow(self): logger.info("Entering overflow state") @@ -745,7 +775,7 @@ def on_enter_overflow(self): def on_enter_overflow_boost(self): logger.info("Entering overflow boost state") self.__encoder.filtration_state("overflow_boost") - self.__actor_halt("Disinfection") + self.__actor_halt(self.__disinfection) self.__devices.get_pump("variable").speed(3) self.__devices.get_pump("boost").on() self.do_delay(self.__boost_duration.total_seconds(), "overflow") @@ -765,19 +795,21 @@ def on_enter_overflow_normal(self): if 0 < speed < 3: self.__disinfection_start() else: - self.__actor_halt("Disinfection") + self.__actor_halt(self.__disinfection) def on_exit_overflow_normal(self): logger.info("Exiting overflow_normal state") - self.__actor_halt("Swim") + self.__actor_halt(self.__swim) def do_repeat_overflow_normal(self): + if self.__check_tank_fault(): + return self.__eco_mode.update(datetime.now(), 1 if self.__speed_overflow > 2 else 0.5) self.do_delay(self.STATE_REFRESH_DELAY, self.do_repeat_overflow_normal.__name__) def on_enter_wash(self): logger.info("Entering wash state") - self.__actor_halt("Disinfection") + self.__actor_halt(self.__disinfection) self.__stir_mode.clear(datetime.now()) def on_enter_wash_backwash(self): @@ -806,10 +838,10 @@ def on_exit_wash_rinse(self): def on_enter_wintering(self): logger.info("Entering wintering state") self.__encoder.filtration_remaining(str(timedelta())) - self.get_actor("Heater").wait.defer() - self.get_actor("Swim").wintering.defer() + self.__heater.wait.defer() + self.__swim.wintering.defer() # open the roller shutter - self.get_actor("Arduino").cover_open.defer() + self.__arduino.cover_open.defer() @do_repeat() def on_enter_wintering_waiting(self): @@ -818,6 +850,8 @@ def on_enter_wintering_waiting(self): self.__devices.get_pump("variable").off() def do_repeat_wintering_waiting(self): + if self.__check_tank_fault(): + return if self.__machine.get_time_in_state() > timedelta(seconds=Filtration.WINTERING_PERIOD): temperature = self.__temperature.get_temperature("temperature_air").get() if temperature is None or temperature <= Filtration.WINTERING_ONLY_BELOW: @@ -833,5 +867,5 @@ def on_enter_wintering_stir(self): def on_exit_wintering(self): logger.info("Exiting wintering state") - self.__actor_halt("Heater") - self.__actor_halt("Swim") + self.__actor_halt(self.__heater) + self.__actor_halt(self.__swim) diff --git a/controller/heating.py b/controller/heating.py index aee5dae..cdd5cd3 100644 --- a/controller/heating.py +++ b/controller/heating.py @@ -73,6 +73,7 @@ def do_repeat_waiting(self): @do_repeat() def on_enter_heating(self): logger.info("Entering heating state") + self.__wants_to_heat = False self.__heater.on() def on_exit_heating(self): @@ -105,6 +106,7 @@ def __call__(self, value): def __init__(self, temperature, encoder, devices): super().__init__() self.__enable = True + self.__wants_to_heat = False self.__temperature = temperature self.__encoder = encoder self.__total_duration = Duration("heating") @@ -118,7 +120,7 @@ def __init__(self, temperature, encoder, devices): self.__machine = PoupoolModel(model=self, states=Heating.states, initial="halt") self.__machine.add_transition("wait", "halt", "waiting") - self.__machine.add_transition("heat", ["halt", "waiting"], "heating", conditions="filtration_allow_heating") + self.__machine.add_transition("heat", ["halt", "waiting"], "heating") self.__machine.add_transition("force", ["halt", "waiting"], "forcing") self.__machine.add_transition("halt", ["waiting", "heating", "forcing", "recovering"], "halt") self.__machine.add_transition("wait", ["heating", "forcing"], "recovering") @@ -156,18 +158,13 @@ def start_hour(self, value): self.__next_start -= timedelta(days=1) logger.info(f"Next heating scheduled for {self.__next_start}") + def wants_to_heat(self): + return self.__wants_to_heat + def min_temp(self, value): self.__min_temp = value logger.info(f"Minimum temperature for heating set to {self.__min_temp}") - def filtration_ready_for_heating(self): - actor = self.get_actor("Filtration") - return actor.is_eco_waiting().get() or actor.is_eco_normal().get() - - def filtration_allow_heating(self): - actor = self.get_actor("Filtration") - return actor.is_heating_running().get() - def on_enter_halt(self): logger.info("Entering halt state") self.__encoder.heating_state("halt") @@ -204,21 +201,14 @@ def do_repeat_waiting(self): self.do_delay(self.STATE_REFRESH_DELAY, self.do_repeat_waiting.__name__) return # Finally we check that filtration is ready to be switched to heating - if not self.filtration_ready_for_heating(): - self.do_delay(self.STATE_REFRESH_DELAY, self.do_repeat_waiting.__name__) - return - # All pre-conditions ok, we can start heating now - self.get_actor("Filtration").heat().get() - # Ensure we are allowed to switch to the heat state. If the transition above fails, - # we will call StopRepeatException but never land in the heating state. - if self.filtration_allow_heating(): - self._proxy.heat.defer() - else: - self.do_delay(self.STATE_REFRESH_DELAY, self.do_repeat_waiting.__name__) + # All pre-conditions ok, we want to start heating now + self.__wants_to_heat = True + self.do_delay(self.STATE_REFRESH_DELAY, self.do_repeat_waiting.__name__) @do_repeat() def on_enter_heating(self): logger.info("Entering heating state") + self.__wants_to_heat = False self.__total_duration.start() self.__encoder.heating_state("heating") self.__devices.get_valve("heating").on() @@ -248,9 +238,6 @@ def on_exit_heating(self): # the next day. If the heating ends normally then we are anyway done for the day. self.__set_next_start() logger.info(f"Heating done for today. Scheduled for {self.__next_start}") - # Only change the filtration state if we are running in heating_running state - if self.filtration_allow_heating(): - self.get_actor("Filtration").heating_delay.defer() def on_enter_forcing(self): logger.info("Entering forcing state") diff --git a/controller/swim.py b/controller/swim.py index ad72da4..e540248 100644 --- a/controller/swim.py +++ b/controller/swim.py @@ -47,6 +47,8 @@ def __init__(self, temperature, encoder, devices): self.__devices = devices self.__timer = Timer("swim") self.__speed = 50 + self.__allow_swim = False + self.__is_wintering = False # Initialize the state machine self.__machine = PoupoolModel(model=self, states=Swim.states, initial="halt") @@ -67,15 +69,15 @@ def speed(self, value): self.__speed = value logger.info(f"Speed for swim pump set to: {self.__speed}") + def set_filtration_state(self, allow_swim, is_wintering): + self.__allow_swim = allow_swim + self.__is_wintering = is_wintering + def filtration_allow_swim(self): - actor = self.get_actor("Filtration") - is_opened = actor.is_overflow_normal().get() or actor.is_standby_normal().get() - is_opened = is_opened or actor.is_comfort().get() - return is_opened or self.filtration_is_wintering() + return self.__allow_swim or self.__is_wintering def filtration_is_wintering(self): - actor = self.get_actor("Filtration") - return actor.is_wintering_waiting().get() or actor.is_wintering_stir().get() + return self.__is_wintering def on_enter_halt(self): logger.info("Entering halt state") diff --git a/controller/tank.py b/controller/tank.py index 42e7fab..46d40aa 100644 --- a/controller/tank.py +++ b/controller/tank.py @@ -47,6 +47,7 @@ def __init__(self, encoder, devices): self.__encoder = encoder self.__devices = devices self.__force_empty = False + self.__fault = False self.levels = self.levels_eco # Initialize the state machine self.__machine = PoupoolModel(model=self, states=Tank.states, initial="halt") @@ -71,11 +72,14 @@ def force_empty(self, value): # In case the user enable the settings and we are running already, we stop everything. # The user can continue from the halt state. logger.warning("The tank is not in the halt state, stopping everything") - self.get_actor("Filtration").halt.defer() + self.__fault = True elif previous and not self.__force_empty and self.is_halt(): # Deactivation of the function, we start the tank FSM. self._proxy.fill.defer() + def is_fault(self): + return self.__fault + def is_force_empty(self): return self.__force_empty @@ -103,7 +107,7 @@ def do_repeat_fill(self): # Security feature: stop if we stay too long in this state if self.__machine.get_time_in_state() > datetime.timedelta(hours=2): logger.warning("Tank TOO LONG in fill state, stopping") - self.get_actor("Filtration").halt.defer() + self.__fault = True return height = self.__get_tank_height() if height > self.levels_too_low: @@ -121,7 +125,7 @@ def do_repeat_low(self): # Security feature: stop if we stay too long in this state if self.__machine.get_time_in_state() > datetime.timedelta(hours=6): logger.warning("Tank TOO LONG in low state, stopping") - self.get_actor("Filtration").halt.defer() + self.__fault = True return height = self.__get_tank_height() if height >= self.levels["low"] + self.hysteresis: @@ -129,7 +133,7 @@ def do_repeat_low(self): return if height < self.levels_too_low: logger.warning(f"Tank TOO LOW, stopping: {height}") - self.get_actor("Filtration").halt.defer() + self.__fault = True return self.do_delay(self.STATE_REFRESH_DELAY / 2, self.do_repeat_low.__name__) diff --git a/poupool.py b/poupool.py index 15ee194..f050b67 100644 --- a/poupool.py +++ b/poupool.py @@ -335,9 +335,6 @@ def main(args, devices): temperature_reader = TemperatureReader.start(sensors).proxy() temperature_writer = TemperatureWriter.start(encoder, temperature_reader).proxy() - # Filtration - filtration = Filtration.start(temperature_reader, encoder, devices).proxy() - # Swimming pump swim = Swim.start(temperature_reader, encoder, devices).proxy() @@ -363,6 +360,11 @@ def main(args, devices): # Cover and water meter arduino = Arduino.start(encoder, devices).proxy() + # Filtration + filtration = Filtration.start( + temperature_reader, encoder, devices, tank, swim, heating, disinfection, arduino, heater, light + ).proxy() + dispatcher.register(filtration, tank, swim, light, heater, heating, disinfection, arduino) # Start actors that run all the time diff --git a/test/test_swim.py b/test/test_swim.py index eff468d..08fb57b 100644 --- a/test/test_swim.py +++ b/test/test_swim.py @@ -1,24 +1,6 @@ -# Poupool - swimming pool control software -# Copyright (C) 2019 Cyril Jaquier -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - from unittest.mock import PropertyMock import pytest -from pykka._threading import ThreadingFuture @pytest.fixture @@ -49,24 +31,16 @@ def swim(mocker, encoder, devices, filtration): from controller.swim import Swim proxy = Swim.start(None, encoder, devices).proxy() - # We suppose get_actor always return the filtration actor - proxy.get_actor = mocker.Mock(return_value=filtration) yield proxy proxy.stop() class TestSwim: - def test_registered(self, swim): - assert swim.get_actor("Swim") is not None - def test_initial_state(self, swim): assert swim.is_halt().get() def test_continuous_state(self, mocker, swim, devices, filtration): - # Filtration - future = mocker.Mock(ThreadingFuture) - filtration.is_overflow_normal = mocker.Mock(return_value=future) - future.get = mocker.Mock(return_value=True) + swim.set_filtration_state(True, False).get() # Devices pump = devices.get_pump("swim") pump.speed = mocker.Mock() @@ -77,8 +51,6 @@ def test_continuous_state(self, mocker, swim, devices, filtration): # Set continuous mode swim.continuous().get() assert swim.is_continuous().get() - pump.speed.assert_called_once_with(pump_speed) # Go back to halt state swim.halt().get() assert swim.is_halt().get() - pump.off.assert_called_once_with()