Add support for STATES of vacuums (#15573)

* Vacuum: Added support for STATES

* Added debug logging and corrected state order

* typo

* Fix travis error, STATE = STATE for readability

* status -> state

* Changed to Entity instead of ToogleEntity

* Updated some vacuums

* Revert changes

* Revert Changes

* added SUPPORT_STATE

* Woof?

* Implement on/off if STATE not supported

* Moved new state vaccum to Class StateVacuumDevice

* Error: I should go to bed

* Moved around methods for easier reading

* Added StateVacuumDevice demo vacuum

* Added tests for StateVacuumDevice demo vacuum

* Fix styling errors

* Refactored to BaseVaccum

* Vacuum will now go back to dock

* Class BaseVacuum is for internal use only

* return -> await

* return -> await
This commit is contained in:
Conrad Juhl Andersen 2018-08-01 14:51:38 +02:00 committed by Jason Hu
parent f8a478946e
commit 2ff5b4ce95
3 changed files with 344 additions and 99 deletions

View File

@ -14,12 +14,12 @@ import voluptuous as vol
from homeassistant.components import group from homeassistant.components import group
from homeassistant.const import ( from homeassistant.const import (
ATTR_BATTERY_LEVEL, ATTR_COMMAND, ATTR_ENTITY_ID, SERVICE_TOGGLE, ATTR_BATTERY_LEVEL, ATTR_COMMAND, ATTR_ENTITY_ID, SERVICE_TOGGLE,
SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_ON) SERVICE_TURN_OFF, SERVICE_TURN_ON, STATE_ON, STATE_PAUSED, STATE_IDLE)
from homeassistant.loader import bind_hass from homeassistant.loader import bind_hass
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity import ToggleEntity from homeassistant.helpers.entity import (ToggleEntity, Entity)
from homeassistant.helpers.icon import icon_for_battery_level from homeassistant.helpers.icon import icon_for_battery_level
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -75,6 +75,13 @@ SERVICE_TO_METHOD = {
'schema': VACUUM_SEND_COMMAND_SERVICE_SCHEMA}, 'schema': VACUUM_SEND_COMMAND_SERVICE_SCHEMA},
} }
STATE_CLEANING = 'cleaning'
STATE_DOCKED = 'docked'
STATE_IDLE = STATE_IDLE
STATE_PAUSED = STATE_PAUSED
STATE_RETURNING = 'returning'
STATE_ERROR = 'error'
DEFAULT_NAME = 'Vacuum cleaner robot' DEFAULT_NAME = 'Vacuum cleaner robot'
SUPPORT_TURN_ON = 1 SUPPORT_TURN_ON = 1
@ -89,6 +96,7 @@ SUPPORT_SEND_COMMAND = 256
SUPPORT_LOCATE = 512 SUPPORT_LOCATE = 512
SUPPORT_CLEAN_SPOT = 1024 SUPPORT_CLEAN_SPOT = 1024
SUPPORT_MAP = 2048 SUPPORT_MAP = 2048
SUPPORT_STATE = 4096
@bind_hass @bind_hass
@ -208,33 +216,22 @@ def async_setup(hass, config):
return True return True
class VacuumDevice(ToggleEntity): class _BaseVacuum(Entity):
"""Representation of a vacuum cleaner robot.""" """Representation of a base vacuum.
Contains common properties and functions for all vacuum devices.
"""
@property @property
def supported_features(self): def supported_features(self):
"""Flag vacuum cleaner features that are supported.""" """Flag vacuum cleaner features that are supported."""
raise NotImplementedError() raise NotImplementedError()
@property
def status(self):
"""Return the status of the vacuum cleaner."""
return None
@property @property
def battery_level(self): def battery_level(self):
"""Return the battery level of the vacuum cleaner.""" """Return the battery level of the vacuum cleaner."""
return None return None
@property
def battery_icon(self):
"""Return the battery icon for the vacuum cleaner."""
charging = False
if self.status is not None:
charging = 'charg' in self.status.lower()
return icon_for_battery_level(
battery_level=self.battery_level, charging=charging)
@property @property
def fan_speed(self): def fan_speed(self):
"""Return the fan speed of the vacuum cleaner.""" """Return the fan speed of the vacuum cleaner."""
@ -245,6 +242,106 @@ class VacuumDevice(ToggleEntity):
"""Get the list of available fan speed steps of the vacuum cleaner.""" """Get the list of available fan speed steps of the vacuum cleaner."""
raise NotImplementedError() 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()
async def async_stop(self, **kwargs):
"""Stop the vacuum cleaner.
This method must be run in the event loop.
"""
await self.hass.async_add_executor_job(partial(self.stop, **kwargs))
def return_to_base(self, **kwargs):
"""Set the vacuum cleaner to return to the dock."""
raise NotImplementedError()
async def async_return_to_base(self, **kwargs):
"""Set the vacuum cleaner to return to the dock.
This method must be run in the event loop.
"""
await self.hass.async_add_executor_job(
partial(self.return_to_base, **kwargs))
def clean_spot(self, **kwargs):
"""Perform a spot clean-up."""
raise NotImplementedError()
async def async_clean_spot(self, **kwargs):
"""Perform a spot clean-up.
This method must be run in the event loop.
"""
await self.hass.async_add_executor_job(
partial(self.clean_spot, **kwargs))
def locate(self, **kwargs):
"""Locate the vacuum cleaner."""
raise NotImplementedError()
async def async_locate(self, **kwargs):
"""Locate the vacuum cleaner.
This method must be run in the event loop.
"""
await self.hass.async_add_executor_job(partial(self.locate, **kwargs))
def set_fan_speed(self, fan_speed, **kwargs):
"""Set fan speed."""
raise NotImplementedError()
async def async_set_fan_speed(self, fan_speed, **kwargs):
"""Set fan speed.
This method must be run in the event loop.
"""
await self.hass.async_add_executor_job(
partial(self.set_fan_speed, fan_speed, **kwargs))
def send_command(self, command, params=None, **kwargs):
"""Send a command to a vacuum cleaner."""
raise NotImplementedError()
async def async_send_command(self, command, params=None, **kwargs):
"""Send a command to a vacuum cleaner.
This method must be run in the event loop.
"""
await self.hass.async_add_executor_job(
partial(self.send_command, command, params=params, **kwargs))
class VacuumDevice(_BaseVacuum, ToggleEntity):
"""Representation of a vacuum cleaner robot."""
@property
def status(self):
"""Return the status of the vacuum cleaner."""
return None
@property
def battery_icon(self):
"""Return the battery icon for the vacuum cleaner."""
charging = False
if self.status is not None:
charging = 'charg' in self.status.lower()
return icon_for_battery_level(
battery_level=self.battery_level, charging=charging)
@property @property
def state_attributes(self): def state_attributes(self):
"""Return the state attributes of the vacuum cleaner.""" """Return the state attributes of the vacuum cleaner."""
@ -267,100 +364,54 @@ class VacuumDevice(ToggleEntity):
"""Turn the vacuum on and start cleaning.""" """Turn the vacuum on and start cleaning."""
raise NotImplementedError() raise NotImplementedError()
def async_turn_on(self, **kwargs): async def async_turn_on(self, **kwargs):
"""Turn the vacuum on and start cleaning. """Turn the vacuum on and start cleaning.
This method must be run in the event loop and returns a coroutine. This method must be run in the event loop.
""" """
return self.hass.async_add_job(partial(self.turn_on, **kwargs)) await self.hass.async_add_executor_job(
partial(self.turn_on, **kwargs))
def turn_off(self, **kwargs): def turn_off(self, **kwargs):
"""Turn the vacuum off stopping the cleaning and returning home.""" """Turn the vacuum off stopping the cleaning and returning home."""
raise NotImplementedError() raise NotImplementedError()
def async_turn_off(self, **kwargs): async def async_turn_off(self, **kwargs):
"""Turn the vacuum off stopping the cleaning and returning home. """Turn the vacuum off stopping the cleaning and returning home.
This method must be run in the event loop and returns a coroutine. This method must be run in the event loop.
""" """
return self.hass.async_add_job(partial(self.turn_off, **kwargs)) await self.hass.async_add_executor_job(
partial(self.turn_off, **kwargs))
def return_to_base(self, **kwargs):
"""Set the vacuum cleaner to return to the dock."""
raise NotImplementedError()
def async_return_to_base(self, **kwargs): class StateVacuumDevice(_BaseVacuum):
"""Set the vacuum cleaner to return to the dock. """Representation of a vacuum cleaner robot that supports states."""
This method must be run in the event loop and returns a coroutine. @property
""" def state(self):
return self.hass.async_add_job(partial(self.return_to_base, **kwargs)) """Return the state of the vacuum cleaner."""
return None
def stop(self, **kwargs): @property
"""Stop the vacuum cleaner.""" def battery_icon(self):
raise NotImplementedError() """Return the battery icon for the vacuum cleaner."""
charging = bool(self.state == STATE_DOCKED)
def async_stop(self, **kwargs): return icon_for_battery_level(
"""Stop the vacuum cleaner. battery_level=self.battery_level, charging=charging)
This method must be run in the event loop and returns a coroutine. @property
""" def state_attributes(self):
return self.hass.async_add_job(partial(self.stop, **kwargs)) """Return the state attributes of the vacuum cleaner."""
data = {}
def clean_spot(self, **kwargs): if self.battery_level is not None:
"""Perform a spot clean-up.""" data[ATTR_BATTERY_LEVEL] = self.battery_level
raise NotImplementedError() data[ATTR_BATTERY_ICON] = self.battery_icon
def async_clean_spot(self, **kwargs): if self.fan_speed is not None:
"""Perform a spot clean-up. data[ATTR_FAN_SPEED] = self.fan_speed
data[ATTR_FAN_SPEED_LIST] = self.fan_speed_list
This method must be run in the event loop and returns a coroutine. return data
"""
return self.hass.async_add_job(partial(self.clean_spot, **kwargs))
def locate(self, **kwargs):
"""Locate the vacuum cleaner."""
raise NotImplementedError()
def async_locate(self, **kwargs):
"""Locate the vacuum cleaner.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(partial(self.locate, **kwargs))
def set_fan_speed(self, fan_speed, **kwargs):
"""Set fan speed."""
raise NotImplementedError()
def async_set_fan_speed(self, fan_speed, **kwargs):
"""Set fan speed.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(
partial(self.set_fan_speed, fan_speed, **kwargs))
def start_pause(self, **kwargs):
"""Start, pause or resume the cleaning task."""
raise NotImplementedError()
def async_start_pause(self, **kwargs):
"""Start, pause or resume the cleaning task.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(
partial(self.start_pause, **kwargs))
def send_command(self, command, params=None, **kwargs):
"""Send a command to a vacuum cleaner."""
raise NotImplementedError()
def async_send_command(self, command, params=None, **kwargs):
"""Send a command to a vacuum cleaner.
This method must be run in the event loop and returns a coroutine.
"""
return self.hass.async_add_job(
partial(self.send_command, command, params=params, **kwargs))

View File

@ -10,7 +10,9 @@ from homeassistant.components.vacuum import (
ATTR_CLEANED_AREA, SUPPORT_BATTERY, SUPPORT_CLEAN_SPOT, ATTR_CLEANED_AREA, SUPPORT_BATTERY, SUPPORT_CLEAN_SPOT,
SUPPORT_FAN_SPEED, SUPPORT_LOCATE, SUPPORT_PAUSE, SUPPORT_RETURN_HOME, SUPPORT_FAN_SPEED, SUPPORT_LOCATE, SUPPORT_PAUSE, SUPPORT_RETURN_HOME,
SUPPORT_SEND_COMMAND, SUPPORT_STATUS, SUPPORT_STOP, SUPPORT_TURN_OFF, SUPPORT_SEND_COMMAND, SUPPORT_STATUS, SUPPORT_STOP, SUPPORT_TURN_OFF,
SUPPORT_TURN_ON, VacuumDevice) SUPPORT_TURN_ON, SUPPORT_STATE, STATE_CLEANING, STATE_DOCKED,
STATE_IDLE, STATE_PAUSED, STATE_RETURNING, VacuumDevice,
StateVacuumDevice)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -28,12 +30,17 @@ SUPPORT_ALL_SERVICES = SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_PAUSE | \
SUPPORT_LOCATE | SUPPORT_STATUS | SUPPORT_BATTERY | \ SUPPORT_LOCATE | SUPPORT_STATUS | SUPPORT_BATTERY | \
SUPPORT_CLEAN_SPOT SUPPORT_CLEAN_SPOT
SUPPORT_STATE_SERVICES = SUPPORT_STATE | SUPPORT_PAUSE | SUPPORT_STOP | \
SUPPORT_RETURN_HOME | SUPPORT_FAN_SPEED | \
SUPPORT_BATTERY | SUPPORT_CLEAN_SPOT
FAN_SPEEDS = ['min', 'medium', 'high', 'max'] FAN_SPEEDS = ['min', 'medium', 'high', 'max']
DEMO_VACUUM_COMPLETE = '0_Ground_floor' DEMO_VACUUM_COMPLETE = '0_Ground_floor'
DEMO_VACUUM_MOST = '1_First_floor' DEMO_VACUUM_MOST = '1_First_floor'
DEMO_VACUUM_BASIC = '2_Second_floor' DEMO_VACUUM_BASIC = '2_Second_floor'
DEMO_VACUUM_MINIMAL = '3_Third_floor' DEMO_VACUUM_MINIMAL = '3_Third_floor'
DEMO_VACUUM_NONE = '4_Fourth_floor' DEMO_VACUUM_NONE = '4_Fourth_floor'
DEMO_VACUUM_STATE = '5_Fifth_floor'
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
@ -44,6 +51,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
DemoVacuum(DEMO_VACUUM_BASIC, SUPPORT_BASIC_SERVICES), DemoVacuum(DEMO_VACUUM_BASIC, SUPPORT_BASIC_SERVICES),
DemoVacuum(DEMO_VACUUM_MINIMAL, SUPPORT_MINIMAL_SERVICES), DemoVacuum(DEMO_VACUUM_MINIMAL, SUPPORT_MINIMAL_SERVICES),
DemoVacuum(DEMO_VACUUM_NONE, 0), DemoVacuum(DEMO_VACUUM_NONE, 0),
StateDemoVacuum(DEMO_VACUUM_STATE),
]) ])
@ -204,3 +212,118 @@ class DemoVacuum(VacuumDevice):
self._status = 'Executing {}({})'.format(command, params) self._status = 'Executing {}({})'.format(command, params)
self._state = True self._state = True
self.schedule_update_ha_state() self.schedule_update_ha_state()
class StateDemoVacuum(StateVacuumDevice):
"""Representation of a demo vacuum supporting states."""
def __init__(self, name):
"""Initialize the vacuum."""
self._name = name
self._supported_features = SUPPORT_STATE_SERVICES
self._state = STATE_DOCKED
self._fan_speed = FAN_SPEEDS[1]
self._cleaned_area = 0
self._battery_level = 100
@property
def name(self):
"""Return the name of the vacuum."""
return self._name
@property
def should_poll(self):
"""No polling needed for a demo vacuum."""
return False
@property
def supported_features(self):
"""Flag supported features."""
return self._supported_features
@property
def state(self):
"""Return the current state of the vacuum."""
return self._state
@property
def battery_level(self):
"""Return the current battery level of the vacuum."""
if self.supported_features & SUPPORT_BATTERY == 0:
return
return max(0, min(100, self._battery_level))
@property
def fan_speed(self):
"""Return the current fan speed of the vacuum."""
if self.supported_features & SUPPORT_FAN_SPEED == 0:
return
return self._fan_speed
@property
def fan_speed_list(self):
"""Return the list of supported fan speeds."""
if self.supported_features & SUPPORT_FAN_SPEED == 0:
return
return FAN_SPEEDS
@property
def device_state_attributes(self):
"""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."""
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()
def stop(self, **kwargs):
"""Stop the cleaning task, do not return to dock."""
if self.supported_features & SUPPORT_STOP == 0:
return
self._state = STATE_IDLE
self.schedule_update_ha_state()
def return_to_base(self, **kwargs):
"""Return dock to charging base."""
if self.supported_features & SUPPORT_RETURN_HOME == 0:
return
self._state = STATE_RETURNING
self.schedule_update_ha_state()
self.hass.loop.call_later(30, self.__set_state_to_dock)
def clean_spot(self, **kwargs):
"""Perform a spot clean-up."""
if self.supported_features & SUPPORT_CLEAN_SPOT == 0:
return
self._state = STATE_CLEANING
self._cleaned_area += 1.32
self._battery_level -= 1
self.schedule_update_ha_state()
def set_fan_speed(self, fan_speed, **kwargs):
"""Set the vacuum's fan speed."""
if self.supported_features & SUPPORT_FAN_SPEED == 0:
return
if fan_speed in self.fan_speed_list:
self._fan_speed = fan_speed
self.schedule_update_ha_state()
def __set_state_to_dock(self):
self._state = STATE_DOCKED
self.schedule_update_ha_state()

View File

@ -6,10 +6,12 @@ from homeassistant.components.vacuum import (
ATTR_BATTERY_LEVEL, ATTR_COMMAND, ATTR_ENTITY_ID, ATTR_FAN_SPEED, ATTR_BATTERY_LEVEL, ATTR_COMMAND, ATTR_ENTITY_ID, ATTR_FAN_SPEED,
ATTR_FAN_SPEED_LIST, ATTR_PARAMS, ATTR_STATUS, DOMAIN, ATTR_FAN_SPEED_LIST, ATTR_PARAMS, ATTR_STATUS, DOMAIN,
ENTITY_ID_ALL_VACUUMS, ENTITY_ID_ALL_VACUUMS,
SERVICE_SEND_COMMAND, SERVICE_SET_FAN_SPEED) SERVICE_SEND_COMMAND, SERVICE_SET_FAN_SPEED,
STATE_DOCKED, STATE_CLEANING, STATE_PAUSED, STATE_IDLE,
STATE_RETURNING)
from homeassistant.components.vacuum.demo import ( from homeassistant.components.vacuum.demo import (
DEMO_VACUUM_BASIC, DEMO_VACUUM_COMPLETE, DEMO_VACUUM_MINIMAL, DEMO_VACUUM_BASIC, DEMO_VACUUM_COMPLETE, DEMO_VACUUM_MINIMAL,
DEMO_VACUUM_MOST, DEMO_VACUUM_NONE, FAN_SPEEDS) DEMO_VACUUM_MOST, DEMO_VACUUM_NONE, DEMO_VACUUM_STATE, FAN_SPEEDS)
from homeassistant.const import ( from homeassistant.const import (
ATTR_SUPPORTED_FEATURES, CONF_PLATFORM, STATE_OFF, STATE_ON) ATTR_SUPPORTED_FEATURES, CONF_PLATFORM, STATE_OFF, STATE_ON)
from homeassistant.setup import setup_component from homeassistant.setup import setup_component
@ -21,6 +23,7 @@ ENTITY_VACUUM_COMPLETE = '{}.{}'.format(DOMAIN, DEMO_VACUUM_COMPLETE).lower()
ENTITY_VACUUM_MINIMAL = '{}.{}'.format(DOMAIN, DEMO_VACUUM_MINIMAL).lower() ENTITY_VACUUM_MINIMAL = '{}.{}'.format(DOMAIN, DEMO_VACUUM_MINIMAL).lower()
ENTITY_VACUUM_MOST = '{}.{}'.format(DOMAIN, DEMO_VACUUM_MOST).lower() ENTITY_VACUUM_MOST = '{}.{}'.format(DOMAIN, DEMO_VACUUM_MOST).lower()
ENTITY_VACUUM_NONE = '{}.{}'.format(DOMAIN, DEMO_VACUUM_NONE).lower() ENTITY_VACUUM_NONE = '{}.{}'.format(DOMAIN, DEMO_VACUUM_NONE).lower()
ENTITY_VACUUM_STATE = '{}.{}'.format(DOMAIN, DEMO_VACUUM_STATE).lower()
class TestVacuumDemo(unittest.TestCase): class TestVacuumDemo(unittest.TestCase):
@ -79,6 +82,14 @@ class TestVacuumDemo(unittest.TestCase):
self.assertEqual(None, state.attributes.get(ATTR_FAN_SPEED_LIST)) self.assertEqual(None, state.attributes.get(ATTR_FAN_SPEED_LIST))
self.assertEqual(STATE_OFF, state.state) 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(STATE_DOCKED, state.state)
self.assertEqual(100, state.attributes.get(ATTR_BATTERY_LEVEL))
self.assertEqual("medium", state.attributes.get(ATTR_FAN_SPEED))
self.assertListEqual(FAN_SPEEDS,
state.attributes.get(ATTR_FAN_SPEED_LIST))
def test_methods(self): def test_methods(self):
"""Test if methods call the services as expected.""" """Test if methods call the services as expected."""
self.hass.states.set(ENTITY_VACUUM_BASIC, STATE_ON) self.hass.states.set(ENTITY_VACUUM_BASIC, STATE_ON)
@ -147,6 +158,41 @@ class TestVacuumDemo(unittest.TestCase):
self.assertIn("spot", state.attributes.get(ATTR_STATUS)) self.assertIn("spot", state.attributes.get(ATTR_STATUS))
self.assertEqual(STATE_ON, state.state) self.assertEqual(STATE_ON, state.state)
vacuum.start_pause(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)
self.hass.block_till_done()
state = self.hass.states.get(ENTITY_VACUUM_STATE)
self.assertEqual(STATE_PAUSED, state.state)
vacuum.stop(self.hass, ENTITY_VACUUM_STATE)
self.hass.block_till_done()
state = self.hass.states.get(ENTITY_VACUUM_STATE)
self.assertEqual(STATE_IDLE, state.state)
state = self.hass.states.get(ENTITY_VACUUM_STATE)
self.assertLess(state.attributes.get(ATTR_BATTERY_LEVEL), 100)
self.assertNotEqual(STATE_DOCKED, state.state)
vacuum.return_to_base(self.hass, ENTITY_VACUUM_STATE)
self.hass.block_till_done()
state = self.hass.states.get(ENTITY_VACUUM_STATE)
self.assertEqual(STATE_RETURNING, state.state)
vacuum.set_fan_speed(self.hass, FAN_SPEEDS[-1],
entity_id=ENTITY_VACUUM_STATE)
self.hass.block_till_done()
state = self.hass.states.get(ENTITY_VACUUM_STATE)
self.assertEqual(FAN_SPEEDS[-1], state.attributes.get(ATTR_FAN_SPEED))
vacuum.clean_spot(self.hass, entity_id=ENTITY_VACUUM_STATE)
self.hass.block_till_done()
state = self.hass.states.get(ENTITY_VACUUM_STATE)
self.assertEqual(STATE_CLEANING, state.state)
def test_unsupported_methods(self): def test_unsupported_methods(self):
"""Test service calls for unsupported vacuums.""" """Test service calls for unsupported vacuums."""
self.hass.states.set(ENTITY_VACUUM_NONE, STATE_ON) self.hass.states.set(ENTITY_VACUUM_NONE, STATE_ON)
@ -201,6 +247,22 @@ class TestVacuumDemo(unittest.TestCase):
self.assertNotIn("spot", state.attributes.get(ATTR_STATUS)) self.assertNotIn("spot", state.attributes.get(ATTR_STATUS))
self.assertEqual(STATE_OFF, state.state) self.assertEqual(STATE_OFF, state.state)
# StateVacuumDevice does not support on/off
vacuum.turn_on(self.hass, entity_id=ENTITY_VACUUM_STATE)
self.hass.block_till_done()
state = self.hass.states.get(ENTITY_VACUUM_STATE)
self.assertNotEqual(STATE_CLEANING, state.state)
vacuum.turn_off(self.hass, entity_id=ENTITY_VACUUM_STATE)
self.hass.block_till_done()
state = self.hass.states.get(ENTITY_VACUUM_STATE)
self.assertNotEqual(STATE_RETURNING, state.state)
vacuum.toggle(self.hass, entity_id=ENTITY_VACUUM_STATE)
self.hass.block_till_done()
state = self.hass.states.get(ENTITY_VACUUM_STATE)
self.assertNotEqual(STATE_CLEANING, state.state)
def test_services(self): def test_services(self):
"""Test vacuum services.""" """Test vacuum services."""
# Test send_command # Test send_command
@ -241,9 +303,11 @@ class TestVacuumDemo(unittest.TestCase):
def test_set_fan_speed(self): def test_set_fan_speed(self):
"""Test vacuum service to set the fan speed.""" """Test vacuum service to set the fan speed."""
group_vacuums = ','.join([ENTITY_VACUUM_BASIC, group_vacuums = ','.join([ENTITY_VACUUM_BASIC,
ENTITY_VACUUM_COMPLETE]) ENTITY_VACUUM_COMPLETE,
ENTITY_VACUUM_STATE])
old_state_basic = self.hass.states.get(ENTITY_VACUUM_BASIC) old_state_basic = self.hass.states.get(ENTITY_VACUUM_BASIC)
old_state_complete = self.hass.states.get(ENTITY_VACUUM_COMPLETE) old_state_complete = self.hass.states.get(ENTITY_VACUUM_COMPLETE)
old_state_state = self.hass.states.get(ENTITY_VACUUM_STATE)
vacuum.set_fan_speed( vacuum.set_fan_speed(
self.hass, FAN_SPEEDS[0], entity_id=group_vacuums) self.hass, FAN_SPEEDS[0], entity_id=group_vacuums)
@ -251,6 +315,7 @@ class TestVacuumDemo(unittest.TestCase):
self.hass.block_till_done() self.hass.block_till_done()
new_state_basic = self.hass.states.get(ENTITY_VACUUM_BASIC) new_state_basic = self.hass.states.get(ENTITY_VACUUM_BASIC)
new_state_complete = self.hass.states.get(ENTITY_VACUUM_COMPLETE) new_state_complete = self.hass.states.get(ENTITY_VACUUM_COMPLETE)
new_state_state = self.hass.states.get(ENTITY_VACUUM_STATE)
self.assertEqual(old_state_basic, new_state_basic) self.assertEqual(old_state_basic, new_state_basic)
self.assertNotIn(ATTR_FAN_SPEED, new_state_basic.attributes) self.assertNotIn(ATTR_FAN_SPEED, new_state_basic.attributes)
@ -261,6 +326,12 @@ class TestVacuumDemo(unittest.TestCase):
self.assertEqual(FAN_SPEEDS[0], self.assertEqual(FAN_SPEEDS[0],
new_state_complete.attributes[ATTR_FAN_SPEED]) new_state_complete.attributes[ATTR_FAN_SPEED])
self.assertNotEqual(old_state_state, new_state_state)
self.assertEqual(FAN_SPEEDS[1],
old_state_state.attributes[ATTR_FAN_SPEED])
self.assertEqual(FAN_SPEEDS[0],
new_state_state.attributes[ATTR_FAN_SPEED])
def test_send_command(self): def test_send_command(self):
"""Test vacuum service to send a command.""" """Test vacuum service to send a command."""
group_vacuums = ','.join([ENTITY_VACUUM_BASIC, group_vacuums = ','.join([ENTITY_VACUUM_BASIC,