From b63312ff2e897565037b04f68cc2c401af828fac Mon Sep 17 00:00:00 2001 From: Conrad Juhl Andersen Date: Fri, 3 Aug 2018 04:49:38 +0200 Subject: [PATCH] Vacuum component: start_pause to individual start and pause commands. (#15751) * Add start and pause to StateVacuumDevice, move start_pause to VacuumDevice * Updated demo vacuum and tests * Add a few more tests --- homeassistant/components/vacuum/__init__.py | 65 +++++++++++++++---- homeassistant/components/vacuum/demo.py | 27 +++++--- homeassistant/components/vacuum/services.yaml | 14 ++++ tests/components/vacuum/test_demo.py | 23 ++++++- 4 files changed, 104 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/vacuum/__init__.py b/homeassistant/components/vacuum/__init__.py index 9cd9fd1c729..97d009626b8 100644 --- a/homeassistant/components/vacuum/__init__.py +++ b/homeassistant/components/vacuum/__init__.py @@ -45,6 +45,8 @@ SERVICE_RETURN_TO_BASE = 'return_to_base' SERVICE_SEND_COMMAND = 'send_command' SERVICE_SET_FAN_SPEED = 'set_fan_speed' SERVICE_START_PAUSE = 'start_pause' +SERVICE_START = 'start' +SERVICE_PAUSE = 'pause' SERVICE_STOP = 'stop' VACUUM_SERVICE_SCHEMA = vol.Schema({ @@ -65,6 +67,8 @@ SERVICE_TO_METHOD = { SERVICE_TURN_OFF: {'method': 'async_turn_off'}, SERVICE_TOGGLE: {'method': 'async_toggle'}, SERVICE_START_PAUSE: {'method': 'async_start_pause'}, + SERVICE_START: {'method': 'async_start'}, + SERVICE_PAUSE: {'method': 'async_pause'}, SERVICE_RETURN_TO_BASE: {'method': 'async_return_to_base'}, SERVICE_CLEAN_SPOT: {'method': 'async_clean_spot'}, SERVICE_LOCATE: {'method': 'async_locate'}, @@ -97,6 +101,7 @@ SUPPORT_LOCATE = 512 SUPPORT_CLEAN_SPOT = 1024 SUPPORT_MAP = 2048 SUPPORT_STATE = 4096 +SUPPORT_START = 8192 @bind_hass @@ -155,6 +160,20 @@ def start_pause(hass, entity_id=None): hass.services.call(DOMAIN, SERVICE_START_PAUSE, data) +@bind_hass +def start(hass, entity_id=None): + """Tell all or specified vacuum to start or resume the current task.""" + data = {ATTR_ENTITY_ID: entity_id} if entity_id else None + hass.services.call(DOMAIN, SERVICE_START, data) + + +@bind_hass +def pause(hass, entity_id=None): + """Tell all or the specified vacuum to pause the current task.""" + data = {ATTR_ENTITY_ID: entity_id} if entity_id else None + hass.services.call(DOMAIN, SERVICE_PAUSE, data) + + @bind_hass def stop(hass, entity_id=None): """Stop all or specified vacuum.""" @@ -242,18 +261,6 @@ class _BaseVacuum(Entity): """Get the list of available fan speed steps of the vacuum cleaner.""" raise NotImplementedError() - def start_pause(self, **kwargs): - """Start, pause or resume the cleaning task.""" - raise NotImplementedError() - - async def async_start_pause(self, **kwargs): - """Start, pause or resume the cleaning task. - - This method must be run in the event loop. - """ - await self.hass.async_add_executor_job( - partial(self.start_pause, **kwargs)) - def stop(self, **kwargs): """Stop the vacuum cleaner.""" raise NotImplementedError() @@ -384,6 +391,18 @@ class VacuumDevice(_BaseVacuum, ToggleEntity): await self.hass.async_add_executor_job( partial(self.turn_off, **kwargs)) + def start_pause(self, **kwargs): + """Start, pause or resume the cleaning task.""" + raise NotImplementedError() + + async def async_start_pause(self, **kwargs): + """Start, pause or resume the cleaning task. + + This method must be run in the event loop. + """ + await self.hass.async_add_executor_job( + partial(self.start_pause, **kwargs)) + class StateVacuumDevice(_BaseVacuum): """Representation of a vacuum cleaner robot that supports states.""" @@ -415,3 +434,25 @@ class StateVacuumDevice(_BaseVacuum): data[ATTR_FAN_SPEED_LIST] = self.fan_speed_list return data + + def start(self): + """Start or resume the cleaning task.""" + raise NotImplementedError() + + async def async_start(self): + """Start or resume the cleaning task. + + This method must be run in the event loop. + """ + await self.hass.async_add_executor_job(self.start) + + def pause(self): + """Pause the cleaning task.""" + raise NotImplementedError() + + async def async_pause(self): + """Pause the cleaning task. + + This method must be run in the event loop. + """ + await self.hass.async_add_executor_job(self.pause) diff --git a/homeassistant/components/vacuum/demo.py b/homeassistant/components/vacuum/demo.py index 737be5e857b..5d4c6856a4d 100644 --- a/homeassistant/components/vacuum/demo.py +++ b/homeassistant/components/vacuum/demo.py @@ -10,8 +10,8 @@ from homeassistant.components.vacuum import ( ATTR_CLEANED_AREA, SUPPORT_BATTERY, SUPPORT_CLEAN_SPOT, SUPPORT_FAN_SPEED, SUPPORT_LOCATE, SUPPORT_PAUSE, SUPPORT_RETURN_HOME, SUPPORT_SEND_COMMAND, SUPPORT_STATUS, SUPPORT_STOP, SUPPORT_TURN_OFF, - SUPPORT_TURN_ON, SUPPORT_STATE, STATE_CLEANING, STATE_DOCKED, - STATE_IDLE, STATE_PAUSED, STATE_RETURNING, VacuumDevice, + SUPPORT_TURN_ON, SUPPORT_STATE, SUPPORT_START, STATE_CLEANING, + STATE_DOCKED, STATE_IDLE, STATE_PAUSED, STATE_RETURNING, VacuumDevice, StateVacuumDevice) _LOGGER = logging.getLogger(__name__) @@ -32,7 +32,7 @@ SUPPORT_ALL_SERVICES = SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PAUSE | \ SUPPORT_STATE_SERVICES = SUPPORT_STATE | SUPPORT_PAUSE | SUPPORT_STOP | \ SUPPORT_RETURN_HOME | SUPPORT_FAN_SPEED | \ - SUPPORT_BATTERY | SUPPORT_CLEAN_SPOT + SUPPORT_BATTERY | SUPPORT_CLEAN_SPOT | SUPPORT_START FAN_SPEEDS = ['min', 'medium', 'high', 'max'] DEMO_VACUUM_COMPLETE = '0_Ground_floor' @@ -274,18 +274,25 @@ class StateDemoVacuum(StateVacuumDevice): """Return device state attributes.""" return {ATTR_CLEANED_AREA: round(self._cleaned_area, 2)} - def start_pause(self, **kwargs): - """Start, pause or resume the cleaning task.""" + def start(self): + """Start or resume the cleaning task.""" + if self.supported_features & SUPPORT_START == 0: + return + + if self._state != STATE_CLEANING: + self._state = STATE_CLEANING + self._cleaned_area += 1.32 + self._battery_level -= 1 + self.schedule_update_ha_state() + + def pause(self): + """Pause the cleaning task.""" if self.supported_features & SUPPORT_PAUSE == 0: return if self._state == STATE_CLEANING: self._state = STATE_PAUSED - else: - self._state = STATE_CLEANING - self._cleaned_area += 1.32 - self._battery_level -= 1 - self.schedule_update_ha_state() + self.schedule_update_ha_state() def stop(self, **kwargs): """Stop the cleaning task, do not return to dock.""" diff --git a/homeassistant/components/vacuum/services.yaml b/homeassistant/components/vacuum/services.yaml index 863157074bc..6e40b3d67fc 100644 --- a/homeassistant/components/vacuum/services.yaml +++ b/homeassistant/components/vacuum/services.yaml @@ -35,6 +35,20 @@ start_pause: description: Name of the vacuum entity. example: 'vacuum.xiaomi_vacuum_cleaner' +start: + description: Start or resume the cleaning task. + fields: + entity_id: + description: Name of the vacuum entity. + example: 'vacuum.xiaomi_vacuum_cleaner' + +pause: + description: Pause the cleaning task. + fields: + entity_id: + description: Name of the vacuum entity. + example: 'vacuum.xiaomi_vacuum_cleaner' + return_to_base: description: Tell the vacuum cleaner to return to its dock. fields: diff --git a/tests/components/vacuum/test_demo.py b/tests/components/vacuum/test_demo.py index b6c96567f50..bd6f2ae543c 100644 --- a/tests/components/vacuum/test_demo.py +++ b/tests/components/vacuum/test_demo.py @@ -83,7 +83,7 @@ class TestVacuumDemo(unittest.TestCase): self.assertEqual(STATE_OFF, state.state) state = self.hass.states.get(ENTITY_VACUUM_STATE) - self.assertEqual(5244, state.attributes.get(ATTR_SUPPORTED_FEATURES)) + self.assertEqual(13436, state.attributes.get(ATTR_SUPPORTED_FEATURES)) self.assertEqual(STATE_DOCKED, state.state) self.assertEqual(100, state.attributes.get(ATTR_BATTERY_LEVEL)) self.assertEqual("medium", state.attributes.get(ATTR_FAN_SPEED)) @@ -158,12 +158,12 @@ class TestVacuumDemo(unittest.TestCase): self.assertIn("spot", state.attributes.get(ATTR_STATUS)) self.assertEqual(STATE_ON, state.state) - vacuum.start_pause(self.hass, ENTITY_VACUUM_STATE) + vacuum.start(self.hass, ENTITY_VACUUM_STATE) self.hass.block_till_done() state = self.hass.states.get(ENTITY_VACUUM_STATE) self.assertEqual(STATE_CLEANING, state.state) - vacuum.start_pause(self.hass, ENTITY_VACUUM_STATE) + vacuum.pause(self.hass, ENTITY_VACUUM_STATE) self.hass.block_till_done() state = self.hass.states.get(ENTITY_VACUUM_STATE) self.assertEqual(STATE_PAUSED, state.state) @@ -247,6 +247,23 @@ class TestVacuumDemo(unittest.TestCase): self.assertNotIn("spot", state.attributes.get(ATTR_STATUS)) self.assertEqual(STATE_OFF, state.state) + # VacuumDevice should not support start and pause methods. + self.hass.states.set(ENTITY_VACUUM_COMPLETE, STATE_ON) + self.hass.block_till_done() + self.assertTrue(vacuum.is_on(self.hass, ENTITY_VACUUM_COMPLETE)) + + vacuum.pause(self.hass, ENTITY_VACUUM_COMPLETE) + self.hass.block_till_done() + self.assertTrue(vacuum.is_on(self.hass, ENTITY_VACUUM_COMPLETE)) + + self.hass.states.set(ENTITY_VACUUM_COMPLETE, STATE_OFF) + self.hass.block_till_done() + self.assertFalse(vacuum.is_on(self.hass, ENTITY_VACUUM_COMPLETE)) + + vacuum.start(self.hass, ENTITY_VACUUM_COMPLETE) + self.hass.block_till_done() + self.assertFalse(vacuum.is_on(self.hass, ENTITY_VACUUM_COMPLETE)) + # StateVacuumDevice does not support on/off vacuum.turn_on(self.hass, entity_id=ENTITY_VACUUM_STATE) self.hass.block_till_done()