From 799e09b92a734f4d74178d2e6313f49169816b31 Mon Sep 17 00:00:00 2001 From: Matt Schmitt Date: Sat, 19 May 2018 18:28:20 -0400 Subject: [PATCH 1/7] Initial commit --- homeassistant/components/homekit/__init__.py | 39 ++++- homeassistant/components/homekit/const.py | 4 + .../components/homekit/type_media_players.py | 148 ++++++++++++++++++ homeassistant/components/homekit/util.py | 24 ++- 4 files changed, 208 insertions(+), 7 deletions(-) create mode 100644 homeassistant/components/homekit/type_media_players.py diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 202f9694689ab..7b6a0a24f0c7e 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -11,9 +11,12 @@ from homeassistant.components.cover import ( SUPPORT_CLOSE, SUPPORT_OPEN, SUPPORT_SET_POSITION) +from homeassistant.components.media_player import ( + SUPPORT_TURN_ON, SUPPORT_TURN_OFF, SUPPORT_PLAY, SUPPORT_PAUSE, + SUPPORT_STOP, SUPPORT_VOLUME_MUTE) from homeassistant.const import ( ATTR_DEVICE_CLASS, ATTR_SUPPORTED_FEATURES, ATTR_UNIT_OF_MEASUREMENT, - CONF_IP_ADDRESS, CONF_NAME, CONF_PORT, + CONF_IP_ADDRESS, CONF_MODE, CONF_NAME, CONF_PORT, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_TEMPERATURE, EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP, TEMP_CELSIUS, TEMP_FAHRENHEIT) @@ -24,7 +27,7 @@ from .const import ( CONF_AUTO_START, CONF_ENTITY_CONFIG, CONF_FILTER, DEFAULT_AUTO_START, DEFAULT_PORT, DEVICE_CLASS_CO2, DEVICE_CLASS_PM25, DOMAIN, HOMEKIT_FILE, - SERVICE_HOMEKIT_START) + ON_OFF, PLAY_PAUSE, PLAY_STOP, SERVICE_HOMEKIT_START, TOGGLE_MUTE) from .util import show_setup_message, validate_entity_config TYPES = Registry() @@ -38,7 +41,6 @@ STATUS_STOPPED = 2 STATUS_WAIT = 3 - CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.All({ vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, @@ -125,6 +127,33 @@ def get_accessory(hass, state, aid, config): elif state.domain == 'lock': a_type = 'Lock' + elif state.domain == 'media_player': + features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) + + supported_modes = [] + if features & (SUPPORT_TURN_ON | SUPPORT_TURN_OFF): + supported_modes.append(ON_OFF) + if features & (SUPPORT_PLAY | SUPPORT_PAUSE): + supported_modes.append(PLAY_PAUSE) + if features & (SUPPORT_PLAY | SUPPORT_STOP): + supported_modes.append(PLAY_STOP) + if features & SUPPORT_VOLUME_MUTE: + supported_modes.append(TOGGLE_MUTE) + + config_modes = config.get(CONF_MODE) + + mode_list = [] + for mode in config_modes: + if mode in supported_modes and mode not in mode_list: + mode_list.append(mode) + if mode not in supported_modes: + _LOGGER.warning('%s does not support mode: %s, supported ' + 'modes are: %s', + state.entity_id, mode, supported_modes) + if mode_list is not None: + config[CONF_MODE] = mode_list + a_type = 'MediaPlayer' + elif state.domain == 'sensor': unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) device_class = state.attributes.get(ATTR_DEVICE_CLASS) @@ -208,8 +237,8 @@ def start(self, *args): # pylint: disable=unused-variable from . import ( # noqa F401 type_covers, type_fans, type_lights, type_locks, - type_security_systems, type_sensors, type_switches, - type_thermostats) + type_media_players, type_security_systems, type_sensors, + type_switches, type_thermostats) for state in self.hass.states.all(): self.add_bridge_accessory(state) diff --git a/homeassistant/components/homekit/const.py b/homeassistant/components/homekit/const.py index 21cad2d9cf71c..d4dc6f97ca5b6 100644 --- a/homeassistant/components/homekit/const.py +++ b/homeassistant/components/homekit/const.py @@ -4,6 +4,10 @@ DOMAIN = 'homekit' HOMEKIT_FILE = '.homekit.state' HOMEKIT_NOTIFY_ID = 4663548 +ON_OFF = 'on_off' +PLAY_PAUSE = 'play_pause' +PLAY_STOP = 'play_stop' +TOGGLE_MUTE = 'toggle_mute' # #### Config #### CONF_AUTO_START = 'auto_start' diff --git a/homeassistant/components/homekit/type_media_players.py b/homeassistant/components/homekit/type_media_players.py new file mode 100644 index 0000000000000..8d4103b1e9a7f --- /dev/null +++ b/homeassistant/components/homekit/type_media_players.py @@ -0,0 +1,148 @@ +"""Class to hold all media player accessories.""" +import logging + +from collections import namedtuple +from pyhap.const import CATEGORY_SWITCH + +from homeassistant.const import ( + ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF, STATE_ON, STATE_OFF, + STATE_PLAYING, STATE_PAUSED, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PAUSE, + STATE_IDLE, CONF_MODE, SERVICE_MEDIA_STOP, SERVICE_VOLUME_MUTE, + STATE_UNKNOWN, CONF_NAME) +from homeassistant.components.media_player import ( + ATTR_MEDIA_VOLUME_MUTED, DOMAIN) + +from . import TYPES +from .accessories import HomeAccessory +from .const import ( + SERV_SWITCH, CHAR_ON, ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE, + CHAR_NAME) + +_LOGGER = logging.getLogger(__name__) + +Mode = namedtuple('Mode', + ['on_state', 'off_state', 'on_service', 'off_service']) + +DEFAULT_MODE_LIST = (ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE) + +STATE_SERVICE_MAP = { + ON_OFF: Mode(STATE_ON, STATE_OFF, SERVICE_TURN_ON, SERVICE_TURN_OFF), + PLAY_PAUSE: Mode(STATE_PLAYING, STATE_PAUSED, SERVICE_MEDIA_PLAY, + SERVICE_MEDIA_PAUSE), + PLAY_STOP: Mode(STATE_PLAYING, STATE_IDLE, SERVICE_MEDIA_PLAY, + SERVICE_MEDIA_STOP), + TOGGLE_MUTE: Mode(True, False, SERVICE_VOLUME_MUTE, SERVICE_VOLUME_MUTE) +} + + +@TYPES.register('MediaPlayer') +class MediaPlayer(HomeAccessory): + """Generate a Media Player accessory.""" + + def __init__(self, *args): + """Initialize a Switch accessory object.""" + super().__init__(*args, category=CATEGORY_SWITCH) + + self.mode_list = self.config.get(CONF_MODE) + self.name = self.config.get(CONF_NAME, self.entity_id) + + if ON_OFF in self.mode_list: + self.on_off_flag_target_state = False + serv_on_off = self.add_preload_service(SERV_SWITCH) + serv_on_off.configure_char( + CHAR_NAME, value='{}_{}'.format(self.name, ON_OFF)) + self.char_on_off = serv_on_off.configure_char( + CHAR_ON, value=False, setter_callback=self.set_on_off) + + if PLAY_PAUSE in self.mode_list: + self.play_pause_flag_target_state = False + serv_play_pause = self.add_preload_service(SERV_SWITCH) + serv_play_pause.configure_char( + CHAR_NAME, value='{}_{}'.format(self.name, PLAY_PAUSE)) + self.char_play_pause = serv_play_pause.configure_char( + CHAR_ON, value=False, setter_callback=self.set_play_pause) + + if PLAY_STOP in self.mode_list: + self.play_stop_flag_target_state = False + serv_play_stop = self.add_preload_service(SERV_SWITCH) + serv_play_stop.configure_char( + CHAR_NAME, value='{}_{}'.format(self.name, PLAY_STOP)) + self.char_play_stop = serv_play_stop.configure_char( + CHAR_ON, value=False, setter_callback=self.set_play_stop) + + if TOGGLE_MUTE in self.mode_list: + self.toggle_mute_flag_target_state = False + serv_toggle_mute = self.add_preload_service(SERV_SWITCH) + serv_toggle_mute.configure_char( + CHAR_NAME, value='{}_{}'.format(self.name, TOGGLE_MUTE)) + self.char_toggle_mute = serv_toggle_mute.configure_char( + CHAR_ON, value=False, setter_callback=self.set_toggle_mute) + + def set_on_off(self, value): + """Move switch state to value if call came from HomeKit.""" + _LOGGER.debug('%s: Set switch state to %s', self.entity_id, value) + self.on_off_flag_target_state = True + service = SERVICE_TURN_ON if value else SERVICE_TURN_OFF + params = {ATTR_ENTITY_ID: self.entity_id} + self.hass.services.call(DOMAIN, service, params) + + def set_play_pause(self, value): + """Move switch state to value if call came from HomeKit.""" + _LOGGER.debug('%s: Set switch state to %s', self.entity_id, value) + self.play_pause_flag_target_state = True + service = SERVICE_MEDIA_PLAY if value else SERVICE_MEDIA_PAUSE + params = {ATTR_ENTITY_ID: self.entity_id} + self.hass.services.call(DOMAIN, service, params) + + def set_play_stop(self, value): + """Move switch state to value if call came from HomeKit.""" + _LOGGER.debug('%s: Set switch state to %s', self.entity_id, value) + self.play_stop_flag_target_state = True + service = SERVICE_MEDIA_PLAY if value else SERVICE_MEDIA_STOP + params = {ATTR_ENTITY_ID: self.entity_id} + self.hass.services.call(DOMAIN, service, params) + + def set_toggle_mute(self, value): + """Move switch state to value if call came from HomeKit.""" + _LOGGER.debug('%s: Set switch state to %s', self.entity_id, value) + self.toggle_mute_flag_target_state = True + params = {ATTR_ENTITY_ID: self.entity_id, + ATTR_MEDIA_VOLUME_MUTED: value} + self.hass.services.call(DOMAIN, SERVICE_VOLUME_MUTE, params) + + def update_state(self, new_state): + """Update switch state after state changed.""" + current_state = new_state.state + + if self.char_on_off: + hk_state = current_state not in [STATE_OFF, STATE_UNKNOWN, 'None'] + if not self.on_off_flag_target_state: + _LOGGER.debug('%s: Set current state to %s', + self.entity_id, hk_state) + self.char_on_off.set_value(hk_state) + self.on_off_flag_target_state = False + + if self.char_play_pause: + hk_state = current_state == STATE_PLAYING + if not self.play_pause_flag_target_state: + _LOGGER.debug('%s: Set current state to %s', + self.entity_id, hk_state) + self.char_play_pause.set_value(hk_state) + self.play_pause_flag_target_state = False + + if self.char_play_stop: + hk_state = current_state == STATE_PLAYING + if not self.play_stop_flag_target_state: + _LOGGER.debug('%s: Set current state to %s', + self.entity_id, hk_state) + self.char_play_stop.set_value(hk_state) + self.play_stop_flag_target_state = False + + if self.char_toggle_mute: + current_state = new_state.attributes.get(ATTR_MEDIA_VOLUME_MUTED) + hk_state = current_state + if not self.toggle_mute_flag_target_state: + _LOGGER.debug('%s: Set current state to %s', + self.entity_id, hk_state) + self.char_toggle_mute.set_value(hk_state) + self.toggle_mute_flag_target_state = False diff --git a/homeassistant/components/homekit/util.py b/homeassistant/components/homekit/util.py index 447257f9e8f25..d1d5b5850ad5d 100644 --- a/homeassistant/components/homekit/util.py +++ b/homeassistant/components/homekit/util.py @@ -5,13 +5,16 @@ from homeassistant.core import split_entity_id from homeassistant.const import ( - ATTR_CODE, CONF_NAME, TEMP_CELSIUS) + ATTR_CODE, CONF_NAME, TEMP_CELSIUS, CONF_MODE) import homeassistant.helpers.config_validation as cv import homeassistant.util.temperature as temp_util -from .const import HOMEKIT_NOTIFY_ID +from .const import ( + HOMEKIT_NOTIFY_ID, ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE) _LOGGER = logging.getLogger(__name__) +DEFAULT_MEDIA_PLAYER_MODES = (ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE) + def validate_entity_config(values): """Validate config entry for CONF_ENTITY.""" @@ -34,6 +37,23 @@ def validate_entity_config(values): code = config.get(ATTR_CODE) params[ATTR_CODE] = cv.string(code) if code else None + if domain == 'media_player': + config_modes = config.get(CONF_MODE) + if config_modes: + config_modes = cv.ensure_list(config_modes) + validated_modes = [] + for mode in config_modes: + if mode in DEFAULT_MEDIA_PLAYER_MODES and mode \ + not in validated_modes: + validated_modes.append(cv.string(mode)) + if mode not in DEFAULT_MEDIA_PLAYER_MODES: + _LOGGER('%s, mode: %s is not a valid mode. Valid ' + 'modes are %s', + entity_id, mode, DEFAULT_MEDIA_PLAYER_MODES) + params[CONF_MODE] = validated_modes + else: + params[CONF_MODE] = DEFAULT_MEDIA_PLAYER_MODES + entities[entity] = params return entities From 6c14d2f8695aa17775f4b1619d1e02ce5b7d50e7 Mon Sep 17 00:00:00 2001 From: Matt Schmitt Date: Sun, 20 May 2018 01:08:35 -0400 Subject: [PATCH 2/7] Style clean up --- homeassistant/components/homekit/__init__.py | 18 ++-- .../components/homekit/accessories.py | 1 + .../components/homekit/type_media_players.py | 86 ++++++++++--------- homeassistant/components/homekit/util.py | 23 +---- 4 files changed, 61 insertions(+), 67 deletions(-) diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 7b6a0a24f0c7e..16aa5d847e2c8 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -41,6 +41,8 @@ STATUS_STOPPED = 2 STATUS_WAIT = 3 +DEFAULT_MEDIA_PLAYER_MODES = (ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE) + CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.All({ vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, @@ -140,18 +142,18 @@ def get_accessory(hass, state, aid, config): if features & SUPPORT_VOLUME_MUTE: supported_modes.append(TOGGLE_MUTE) - config_modes = config.get(CONF_MODE) + config_modes = config.get(CONF_MODE, DEFAULT_MEDIA_PLAYER_MODES) - mode_list = [] + validated_modes = [] for mode in config_modes: - if mode in supported_modes and mode not in mode_list: - mode_list.append(mode) + if mode in supported_modes and mode not in validated_modes: + validated_modes.append(mode) if mode not in supported_modes: - _LOGGER.warning('%s does not support mode: %s, supported ' - 'modes are: %s', + _LOGGER.warning('The entity "%s" does not support ' + 'mode: "%s", supported modes are: %s', state.entity_id, mode, supported_modes) - if mode_list is not None: - config[CONF_MODE] = mode_list + if validated_modes is not None: + config[CONF_MODE] = validated_modes a_type = 'MediaPlayer' elif state.domain == 'sensor': diff --git a/homeassistant/components/homekit/accessories.py b/homeassistant/components/homekit/accessories.py index ded4526b0086a..f9d68c242ccfb 100644 --- a/homeassistant/components/homekit/accessories.py +++ b/homeassistant/components/homekit/accessories.py @@ -76,6 +76,7 @@ def __init__(self, hass, name, entity_id, aid, config, self.config = config self.entity_id = entity_id self.hass = hass + self.name = name def run(self): """Method called by accessory after driver is started.""" diff --git a/homeassistant/components/homekit/type_media_players.py b/homeassistant/components/homekit/type_media_players.py index 8d4103b1e9a7f..c4bf5e7cb09de 100644 --- a/homeassistant/components/homekit/type_media_players.py +++ b/homeassistant/components/homekit/type_media_players.py @@ -1,14 +1,12 @@ """Class to hold all media player accessories.""" import logging -from collections import namedtuple from pyhap.const import CATEGORY_SWITCH from homeassistant.const import ( - ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF, STATE_ON, STATE_OFF, - STATE_PLAYING, STATE_PAUSED, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PAUSE, - STATE_IDLE, CONF_MODE, SERVICE_MEDIA_STOP, SERVICE_VOLUME_MUTE, - STATE_UNKNOWN, CONF_NAME) + ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF, STATE_OFF, + STATE_PLAYING, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PAUSE, + CONF_MODE, SERVICE_MEDIA_STOP, SERVICE_VOLUME_MUTE, STATE_UNKNOWN) from homeassistant.components.media_player import ( ATTR_MEDIA_VOLUME_MUTED, DOMAIN) @@ -20,19 +18,10 @@ _LOGGER = logging.getLogger(__name__) -Mode = namedtuple('Mode', - ['on_state', 'off_state', 'on_service', 'off_service']) - -DEFAULT_MODE_LIST = (ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE) - -STATE_SERVICE_MAP = { - ON_OFF: Mode(STATE_ON, STATE_OFF, SERVICE_TURN_ON, SERVICE_TURN_OFF), - PLAY_PAUSE: Mode(STATE_PLAYING, STATE_PAUSED, SERVICE_MEDIA_PLAY, - SERVICE_MEDIA_PAUSE), - PLAY_STOP: Mode(STATE_PLAYING, STATE_IDLE, SERVICE_MEDIA_PLAY, - SERVICE_MEDIA_STOP), - TOGGLE_MUTE: Mode(True, False, SERVICE_VOLUME_MUTE, SERVICE_VOLUME_MUTE) -} +MODE_FRIENDLY_NAME = {ON_OFF: 'Power', + PLAY_PAUSE: 'Play/Pause', + PLAY_STOP: 'Play/Stop', + TOGGLE_MUTE: 'Mute'} @TYPES.register('MediaPlayer') @@ -42,45 +31,58 @@ class MediaPlayer(HomeAccessory): def __init__(self, *args): """Initialize a Switch accessory object.""" super().__init__(*args, category=CATEGORY_SWITCH) + self.on_off_flag_target_state = False + self.play_pause_flag_target_state = False + self.play_stop_flag_target_state = False + self.toggle_mute_flag_target_state = False + + self.char_on_off = None + self.char_play_pause = None + self.char_play_stop = None + self.char_toggle_mute = None self.mode_list = self.config.get(CONF_MODE) - self.name = self.config.get(CONF_NAME, self.entity_id) if ON_OFF in self.mode_list: - self.on_off_flag_target_state = False - serv_on_off = self.add_preload_service(SERV_SWITCH) + serv_on_off = self.add_preload_service( + SERV_SWITCH, chars=CHAR_NAME) serv_on_off.configure_char( - CHAR_NAME, value='{}_{}'.format(self.name, ON_OFF)) + CHAR_NAME, value=self.generate_service_name(ON_OFF)) self.char_on_off = serv_on_off.configure_char( CHAR_ON, value=False, setter_callback=self.set_on_off) if PLAY_PAUSE in self.mode_list: - self.play_pause_flag_target_state = False - serv_play_pause = self.add_preload_service(SERV_SWITCH) + serv_play_pause = self.add_preload_service( + SERV_SWITCH, chars=CHAR_NAME) serv_play_pause.configure_char( - CHAR_NAME, value='{}_{}'.format(self.name, PLAY_PAUSE)) + CHAR_NAME, value=self.generate_service_name(PLAY_PAUSE)) self.char_play_pause = serv_play_pause.configure_char( CHAR_ON, value=False, setter_callback=self.set_play_pause) if PLAY_STOP in self.mode_list: - self.play_stop_flag_target_state = False - serv_play_stop = self.add_preload_service(SERV_SWITCH) + serv_play_stop = self.add_preload_service( + SERV_SWITCH, chars=CHAR_NAME) serv_play_stop.configure_char( - CHAR_NAME, value='{}_{}'.format(self.name, PLAY_STOP)) + CHAR_NAME, value=self.generate_service_name(PLAY_STOP)) self.char_play_stop = serv_play_stop.configure_char( CHAR_ON, value=False, setter_callback=self.set_play_stop) if TOGGLE_MUTE in self.mode_list: - self.toggle_mute_flag_target_state = False - serv_toggle_mute = self.add_preload_service(SERV_SWITCH) + serv_toggle_mute = self.add_preload_service( + SERV_SWITCH, chars=CHAR_NAME) serv_toggle_mute.configure_char( - CHAR_NAME, value='{}_{}'.format(self.name, TOGGLE_MUTE)) + CHAR_NAME, value=self.generate_service_name(TOGGLE_MUTE)) self.char_toggle_mute = serv_toggle_mute.configure_char( CHAR_ON, value=False, setter_callback=self.set_toggle_mute) + def generate_service_name(self, mode): + """Generate name for individual service.""" + return '{} {}'.format(self.name, MODE_FRIENDLY_NAME[mode]) + def set_on_off(self, value): """Move switch state to value if call came from HomeKit.""" - _LOGGER.debug('%s: Set switch state to %s', self.entity_id, value) + _LOGGER.debug('%s: Set switch state for "on_off" to %s', + self.entity_id, value) self.on_off_flag_target_state = True service = SERVICE_TURN_ON if value else SERVICE_TURN_OFF params = {ATTR_ENTITY_ID: self.entity_id} @@ -88,7 +90,8 @@ def set_on_off(self, value): def set_play_pause(self, value): """Move switch state to value if call came from HomeKit.""" - _LOGGER.debug('%s: Set switch state to %s', self.entity_id, value) + _LOGGER.debug('%s: Set switch state for "play_pause" to %s', + self.entity_id, value) self.play_pause_flag_target_state = True service = SERVICE_MEDIA_PLAY if value else SERVICE_MEDIA_PAUSE params = {ATTR_ENTITY_ID: self.entity_id} @@ -96,7 +99,8 @@ def set_play_pause(self, value): def set_play_stop(self, value): """Move switch state to value if call came from HomeKit.""" - _LOGGER.debug('%s: Set switch state to %s', self.entity_id, value) + _LOGGER.debug('%s: Set switch state for "play_stop" to %s', + self.entity_id, value) self.play_stop_flag_target_state = True service = SERVICE_MEDIA_PLAY if value else SERVICE_MEDIA_STOP params = {ATTR_ENTITY_ID: self.entity_id} @@ -104,7 +108,8 @@ def set_play_stop(self, value): def set_toggle_mute(self, value): """Move switch state to value if call came from HomeKit.""" - _LOGGER.debug('%s: Set switch state to %s', self.entity_id, value) + _LOGGER.debug('%s: Set switch state for "toggle_mute" to %s', + self.entity_id, value) self.toggle_mute_flag_target_state = True params = {ATTR_ENTITY_ID: self.entity_id, ATTR_MEDIA_VOLUME_MUTED: value} @@ -115,9 +120,10 @@ def update_state(self, new_state): current_state = new_state.state if self.char_on_off: - hk_state = current_state not in [STATE_OFF, STATE_UNKNOWN, 'None'] + hk_state = current_state \ + not in [STATE_OFF, STATE_UNKNOWN, 'None'] if not self.on_off_flag_target_state: - _LOGGER.debug('%s: Set current state to %s', + _LOGGER.debug('%s: Set current state for "on_off" to %s', self.entity_id, hk_state) self.char_on_off.set_value(hk_state) self.on_off_flag_target_state = False @@ -125,7 +131,7 @@ def update_state(self, new_state): if self.char_play_pause: hk_state = current_state == STATE_PLAYING if not self.play_pause_flag_target_state: - _LOGGER.debug('%s: Set current state to %s', + _LOGGER.debug('%s: Set current state for "play_pause" to %s', self.entity_id, hk_state) self.char_play_pause.set_value(hk_state) self.play_pause_flag_target_state = False @@ -133,7 +139,7 @@ def update_state(self, new_state): if self.char_play_stop: hk_state = current_state == STATE_PLAYING if not self.play_stop_flag_target_state: - _LOGGER.debug('%s: Set current state to %s', + _LOGGER.debug('%s: Set current state for "play_stop" to %s', self.entity_id, hk_state) self.char_play_stop.set_value(hk_state) self.play_stop_flag_target_state = False @@ -142,7 +148,7 @@ def update_state(self, new_state): current_state = new_state.attributes.get(ATTR_MEDIA_VOLUME_MUTED) hk_state = current_state if not self.toggle_mute_flag_target_state: - _LOGGER.debug('%s: Set current state to %s', + _LOGGER.debug('%s: Set current state for "toggle_mute" to %s', self.entity_id, hk_state) self.char_toggle_mute.set_value(hk_state) self.toggle_mute_flag_target_state = False diff --git a/homeassistant/components/homekit/util.py b/homeassistant/components/homekit/util.py index d1d5b5850ad5d..7b47a1df4145e 100644 --- a/homeassistant/components/homekit/util.py +++ b/homeassistant/components/homekit/util.py @@ -8,13 +8,10 @@ ATTR_CODE, CONF_NAME, TEMP_CELSIUS, CONF_MODE) import homeassistant.helpers.config_validation as cv import homeassistant.util.temperature as temp_util -from .const import ( - HOMEKIT_NOTIFY_ID, ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE) +from .const import HOMEKIT_NOTIFY_ID _LOGGER = logging.getLogger(__name__) -DEFAULT_MEDIA_PLAYER_MODES = (ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE) - def validate_entity_config(values): """Validate config entry for CONF_ENTITY.""" @@ -38,21 +35,9 @@ def validate_entity_config(values): params[ATTR_CODE] = cv.string(code) if code else None if domain == 'media_player': - config_modes = config.get(CONF_MODE) - if config_modes: - config_modes = cv.ensure_list(config_modes) - validated_modes = [] - for mode in config_modes: - if mode in DEFAULT_MEDIA_PLAYER_MODES and mode \ - not in validated_modes: - validated_modes.append(cv.string(mode)) - if mode not in DEFAULT_MEDIA_PLAYER_MODES: - _LOGGER('%s, mode: %s is not a valid mode. Valid ' - 'modes are %s', - entity_id, mode, DEFAULT_MEDIA_PLAYER_MODES) - params[CONF_MODE] = validated_modes - else: - params[CONF_MODE] = DEFAULT_MEDIA_PLAYER_MODES + mode = config.get(CONF_MODE) + if mode: + params[CONF_MODE] = cv.ensure_list(mode) entities[entity] = params return entities From 860ab261671cdd6c1d9c155093ccb03be82efb73 Mon Sep 17 00:00:00 2001 From: Matt Schmitt Date: Sun, 20 May 2018 14:16:47 -0400 Subject: [PATCH 3/7] Move mode validation to util and update tests --- homeassistant/components/homekit/__init__.py | 33 +----- .../components/homekit/accessories.py | 1 - homeassistant/components/homekit/const.py | 10 +- .../components/homekit/type_media_players.py | 96 ++++++++-------- homeassistant/components/homekit/util.py | 42 ++++++- .../homekit/test_get_accessories.py | 9 +- .../homekit/test_type_media_players.py | 105 ++++++++++++++++++ tests/components/homekit/test_util.py | 26 ++++- 8 files changed, 230 insertions(+), 92 deletions(-) create mode 100644 tests/components/homekit/test_type_media_players.py diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 16aa5d847e2c8..b8a028146657e 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -11,9 +11,6 @@ from homeassistant.components.cover import ( SUPPORT_CLOSE, SUPPORT_OPEN, SUPPORT_SET_POSITION) -from homeassistant.components.media_player import ( - SUPPORT_TURN_ON, SUPPORT_TURN_OFF, SUPPORT_PLAY, SUPPORT_PAUSE, - SUPPORT_STOP, SUPPORT_VOLUME_MUTE) from homeassistant.const import ( ATTR_DEVICE_CLASS, ATTR_SUPPORTED_FEATURES, ATTR_UNIT_OF_MEASUREMENT, CONF_IP_ADDRESS, CONF_MODE, CONF_NAME, CONF_PORT, @@ -28,7 +25,8 @@ CONF_AUTO_START, CONF_ENTITY_CONFIG, CONF_FILTER, DEFAULT_AUTO_START, DEFAULT_PORT, DEVICE_CLASS_CO2, DEVICE_CLASS_PM25, DOMAIN, HOMEKIT_FILE, ON_OFF, PLAY_PAUSE, PLAY_STOP, SERVICE_HOMEKIT_START, TOGGLE_MUTE) -from .util import show_setup_message, validate_entity_config +from .util import ( + show_setup_message, validate_entity_config, validate_media_player_modes) TYPES = Registry() _LOGGER = logging.getLogger(__name__) @@ -41,7 +39,6 @@ STATUS_STOPPED = 2 STATUS_WAIT = 3 -DEFAULT_MEDIA_PLAYER_MODES = (ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE) CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.All({ @@ -130,30 +127,8 @@ def get_accessory(hass, state, aid, config): a_type = 'Lock' elif state.domain == 'media_player': - features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) - - supported_modes = [] - if features & (SUPPORT_TURN_ON | SUPPORT_TURN_OFF): - supported_modes.append(ON_OFF) - if features & (SUPPORT_PLAY | SUPPORT_PAUSE): - supported_modes.append(PLAY_PAUSE) - if features & (SUPPORT_PLAY | SUPPORT_STOP): - supported_modes.append(PLAY_STOP) - if features & SUPPORT_VOLUME_MUTE: - supported_modes.append(TOGGLE_MUTE) - - config_modes = config.get(CONF_MODE, DEFAULT_MEDIA_PLAYER_MODES) - - validated_modes = [] - for mode in config_modes: - if mode in supported_modes and mode not in validated_modes: - validated_modes.append(mode) - if mode not in supported_modes: - _LOGGER.warning('The entity "%s" does not support ' - 'mode: "%s", supported modes are: %s', - state.entity_id, mode, supported_modes) - if validated_modes is not None: - config[CONF_MODE] = validated_modes + config[CONF_MODE] = validate_media_player_modes(state, config) + if config[CONF_MODE] is not None: a_type = 'MediaPlayer' elif state.domain == 'sensor': diff --git a/homeassistant/components/homekit/accessories.py b/homeassistant/components/homekit/accessories.py index f9d68c242ccfb..ded4526b0086a 100644 --- a/homeassistant/components/homekit/accessories.py +++ b/homeassistant/components/homekit/accessories.py @@ -76,7 +76,6 @@ def __init__(self, hass, name, entity_id, aid, config, self.config = config self.entity_id = entity_id self.hass = hass - self.name = name def run(self): """Method called by accessory after driver is started.""" diff --git a/homeassistant/components/homekit/const.py b/homeassistant/components/homekit/const.py index d4dc6f97ca5b6..f59ee5488ecd9 100644 --- a/homeassistant/components/homekit/const.py +++ b/homeassistant/components/homekit/const.py @@ -4,10 +4,6 @@ DOMAIN = 'homekit' HOMEKIT_FILE = '.homekit.state' HOMEKIT_NOTIFY_ID = 4663548 -ON_OFF = 'on_off' -PLAY_PAUSE = 'play_pause' -PLAY_STOP = 'play_stop' -TOGGLE_MUTE = 'toggle_mute' # #### Config #### CONF_AUTO_START = 'auto_start' @@ -27,6 +23,12 @@ BRIDGE_SERIAL_NUMBER = 'homekit.bridge' MANUFACTURER = 'Home Assistant' +# #### Media Player Modes #### +ON_OFF = 'on_off' +PLAY_PAUSE = 'play_pause' +PLAY_STOP = 'play_stop' +TOGGLE_MUTE = 'toggle_mute' + # #### Services #### SERV_ACCESSORY_INFO = 'AccessoryInformation' SERV_AIR_QUALITY_SENSOR = 'AirQualitySensor' diff --git a/homeassistant/components/homekit/type_media_players.py b/homeassistant/components/homekit/type_media_players.py index c4bf5e7cb09de..caf8cce2b8c77 100644 --- a/homeassistant/components/homekit/type_media_players.py +++ b/homeassistant/components/homekit/type_media_players.py @@ -4,17 +4,17 @@ from pyhap.const import CATEGORY_SWITCH from homeassistant.const import ( - ATTR_ENTITY_ID, SERVICE_TURN_ON, SERVICE_TURN_OFF, STATE_OFF, - STATE_PLAYING, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PAUSE, - CONF_MODE, SERVICE_MEDIA_STOP, SERVICE_VOLUME_MUTE, STATE_UNKNOWN) + ATTR_ENTITY_ID, CONF_MODE, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY, + SERVICE_MEDIA_STOP, SERVICE_TURN_OFF, SERVICE_TURN_ON, SERVICE_VOLUME_MUTE, + STATE_OFF, STATE_PLAYING, STATE_UNKNOWN) from homeassistant.components.media_player import ( ATTR_MEDIA_VOLUME_MUTED, DOMAIN) from . import TYPES from .accessories import HomeAccessory from .const import ( - SERV_SWITCH, CHAR_ON, ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE, - CHAR_NAME) + CHAR_NAME, CHAR_ON, ON_OFF, PLAY_PAUSE, PLAY_STOP, SERV_SWITCH, + TOGGLE_MUTE) _LOGGER = logging.getLogger(__name__) @@ -31,59 +31,51 @@ class MediaPlayer(HomeAccessory): def __init__(self, *args): """Initialize a Switch accessory object.""" super().__init__(*args, category=CATEGORY_SWITCH) - self.on_off_flag_target_state = False - self.play_pause_flag_target_state = False - self.play_stop_flag_target_state = False - self.toggle_mute_flag_target_state = False + self._flag = {ON_OFF: False, PLAY_PAUSE: False, + PLAY_STOP: False, TOGGLE_MUTE: False} - self.char_on_off = None - self.char_play_pause = None - self.char_play_stop = None - self.char_toggle_mute = None + self.chars = {ON_OFF: None, PLAY_PAUSE: None, + PLAY_STOP: None, TOGGLE_MUTE: None} - self.mode_list = self.config.get(CONF_MODE) + modes = self.config.get(CONF_MODE) - if ON_OFF in self.mode_list: - serv_on_off = self.add_preload_service( - SERV_SWITCH, chars=CHAR_NAME) + if ON_OFF in modes: + serv_on_off = self.add_preload_service(SERV_SWITCH, CHAR_NAME) serv_on_off.configure_char( CHAR_NAME, value=self.generate_service_name(ON_OFF)) - self.char_on_off = serv_on_off.configure_char( + self.chars[ON_OFF] = serv_on_off.configure_char( CHAR_ON, value=False, setter_callback=self.set_on_off) - if PLAY_PAUSE in self.mode_list: - serv_play_pause = self.add_preload_service( - SERV_SWITCH, chars=CHAR_NAME) + if PLAY_PAUSE in modes: + serv_play_pause = self.add_preload_service(SERV_SWITCH, CHAR_NAME) serv_play_pause.configure_char( CHAR_NAME, value=self.generate_service_name(PLAY_PAUSE)) - self.char_play_pause = serv_play_pause.configure_char( + self.chars[PLAY_PAUSE] = serv_play_pause.configure_char( CHAR_ON, value=False, setter_callback=self.set_play_pause) - if PLAY_STOP in self.mode_list: - serv_play_stop = self.add_preload_service( - SERV_SWITCH, chars=CHAR_NAME) + if PLAY_STOP in modes: + serv_play_stop = self.add_preload_service(SERV_SWITCH, CHAR_NAME) serv_play_stop.configure_char( CHAR_NAME, value=self.generate_service_name(PLAY_STOP)) - self.char_play_stop = serv_play_stop.configure_char( + self.chars[PLAY_STOP] = serv_play_stop.configure_char( CHAR_ON, value=False, setter_callback=self.set_play_stop) - if TOGGLE_MUTE in self.mode_list: - serv_toggle_mute = self.add_preload_service( - SERV_SWITCH, chars=CHAR_NAME) + if TOGGLE_MUTE in modes: + serv_toggle_mute = self.add_preload_service(SERV_SWITCH, CHAR_NAME) serv_toggle_mute.configure_char( CHAR_NAME, value=self.generate_service_name(TOGGLE_MUTE)) - self.char_toggle_mute = serv_toggle_mute.configure_char( + self.chars[TOGGLE_MUTE] = serv_toggle_mute.configure_char( CHAR_ON, value=False, setter_callback=self.set_toggle_mute) def generate_service_name(self, mode): """Generate name for individual service.""" - return '{} {}'.format(self.name, MODE_FRIENDLY_NAME[mode]) + return '{} {}'.format(self.display_name, MODE_FRIENDLY_NAME[mode]) def set_on_off(self, value): """Move switch state to value if call came from HomeKit.""" _LOGGER.debug('%s: Set switch state for "on_off" to %s', self.entity_id, value) - self.on_off_flag_target_state = True + self._flag[ON_OFF] = True service = SERVICE_TURN_ON if value else SERVICE_TURN_OFF params = {ATTR_ENTITY_ID: self.entity_id} self.hass.services.call(DOMAIN, service, params) @@ -92,7 +84,7 @@ def set_play_pause(self, value): """Move switch state to value if call came from HomeKit.""" _LOGGER.debug('%s: Set switch state for "play_pause" to %s', self.entity_id, value) - self.play_pause_flag_target_state = True + self._flag[PLAY_PAUSE] = True service = SERVICE_MEDIA_PLAY if value else SERVICE_MEDIA_PAUSE params = {ATTR_ENTITY_ID: self.entity_id} self.hass.services.call(DOMAIN, service, params) @@ -101,7 +93,7 @@ def set_play_stop(self, value): """Move switch state to value if call came from HomeKit.""" _LOGGER.debug('%s: Set switch state for "play_stop" to %s', self.entity_id, value) - self.play_stop_flag_target_state = True + self._flag[PLAY_STOP] = True service = SERVICE_MEDIA_PLAY if value else SERVICE_MEDIA_STOP params = {ATTR_ENTITY_ID: self.entity_id} self.hass.services.call(DOMAIN, service, params) @@ -110,7 +102,7 @@ def set_toggle_mute(self, value): """Move switch state to value if call came from HomeKit.""" _LOGGER.debug('%s: Set switch state for "toggle_mute" to %s', self.entity_id, value) - self.toggle_mute_flag_target_state = True + self._flag[TOGGLE_MUTE] = True params = {ATTR_ENTITY_ID: self.entity_id, ATTR_MEDIA_VOLUME_MUTED: value} self.hass.services.call(DOMAIN, SERVICE_VOLUME_MUTE, params) @@ -119,36 +111,36 @@ def update_state(self, new_state): """Update switch state after state changed.""" current_state = new_state.state - if self.char_on_off: + if self.chars[ON_OFF]: hk_state = current_state \ - not in [STATE_OFF, STATE_UNKNOWN, 'None'] - if not self.on_off_flag_target_state: + not in (STATE_OFF, STATE_UNKNOWN, 'None') + if not self._flag[ON_OFF]: _LOGGER.debug('%s: Set current state for "on_off" to %s', self.entity_id, hk_state) - self.char_on_off.set_value(hk_state) - self.on_off_flag_target_state = False + self.chars[ON_OFF].set_value(hk_state) + self._flag[ON_OFF] = False - if self.char_play_pause: + if self.chars[PLAY_PAUSE]: hk_state = current_state == STATE_PLAYING - if not self.play_pause_flag_target_state: + if not self._flag[PLAY_PAUSE]: _LOGGER.debug('%s: Set current state for "play_pause" to %s', self.entity_id, hk_state) - self.char_play_pause.set_value(hk_state) - self.play_pause_flag_target_state = False + self.chars[PLAY_PAUSE].set_value(hk_state) + self._flag[PLAY_PAUSE] = False - if self.char_play_stop: + if self.chars[PLAY_STOP]: hk_state = current_state == STATE_PLAYING - if not self.play_stop_flag_target_state: + if not self._flag[PLAY_STOP]: _LOGGER.debug('%s: Set current state for "play_stop" to %s', self.entity_id, hk_state) - self.char_play_stop.set_value(hk_state) - self.play_stop_flag_target_state = False + self.chars[PLAY_STOP].set_value(hk_state) + self._flag[PLAY_STOP] = False - if self.char_toggle_mute: + if self.chars[TOGGLE_MUTE]: current_state = new_state.attributes.get(ATTR_MEDIA_VOLUME_MUTED) hk_state = current_state - if not self.toggle_mute_flag_target_state: + if not self._flag[TOGGLE_MUTE]: _LOGGER.debug('%s: Set current state for "toggle_mute" to %s', self.entity_id, hk_state) - self.char_toggle_mute.set_value(hk_state) - self.toggle_mute_flag_target_state = False + self.chars[TOGGLE_MUTE].set_value(hk_state) + self._flag[TOGGLE_MUTE] = False diff --git a/homeassistant/components/homekit/util.py b/homeassistant/components/homekit/util.py index 7b47a1df4145e..08dfcf0462689 100644 --- a/homeassistant/components/homekit/util.py +++ b/homeassistant/components/homekit/util.py @@ -3,15 +3,21 @@ import voluptuous as vol +from homeassistant.components.media_player import ( + SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_STOP, SUPPORT_TURN_OFF, + SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE) from homeassistant.core import split_entity_id from homeassistant.const import ( - ATTR_CODE, CONF_NAME, TEMP_CELSIUS, CONF_MODE) + ATTR_CODE, ATTR_SUPPORTED_FEATURES, CONF_MODE, CONF_NAME, TEMP_CELSIUS) import homeassistant.helpers.config_validation as cv import homeassistant.util.temperature as temp_util -from .const import HOMEKIT_NOTIFY_ID +from .const import ( + HOMEKIT_NOTIFY_ID, ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE) _LOGGER = logging.getLogger(__name__) +MEDIA_PLAYER_MODES = (ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE) + def validate_entity_config(values): """Validate config entry for CONF_ENTITY.""" @@ -38,11 +44,43 @@ def validate_entity_config(values): mode = config.get(CONF_MODE) if mode: params[CONF_MODE] = cv.ensure_list(mode) + for mode in params[CONF_MODE]: + if mode not in MEDIA_PLAYER_MODES: + raise vol.Invalid( + 'Invalid mode: "{}", valid modes are: "{}".' + .format(mode, MEDIA_PLAYER_MODES)) entities[entity] = params return entities +def validate_media_player_modes(state, config): + """Validate modes for media playeres.""" + features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) + + supported_modes = [] + if features & (SUPPORT_TURN_ON | SUPPORT_TURN_OFF): + supported_modes.append(ON_OFF) + if features & (SUPPORT_PLAY | SUPPORT_PAUSE): + supported_modes.append(PLAY_PAUSE) + if features & (SUPPORT_PLAY | SUPPORT_STOP): + supported_modes.append(PLAY_STOP) + if features & SUPPORT_VOLUME_MUTE: + supported_modes.append(TOGGLE_MUTE) + + config_modes = config.get(CONF_MODE, MEDIA_PLAYER_MODES) + + validated_modes = [] + for mode in config_modes: + if mode in supported_modes and mode not in validated_modes: + validated_modes.append(mode) + if mode not in supported_modes: + _LOGGER.warning('The entity "%s" does not support ' + 'mode: "%s", supported modes are: %s', + state.entity_id, mode, supported_modes) + return validated_modes + + def show_setup_message(hass, pincode): """Display persistent notification with setup information.""" pin = pincode.decode() diff --git a/tests/components/homekit/test_get_accessories.py b/tests/components/homekit/test_get_accessories.py index 25a0dd3f1cb78..d4d769a419197 100644 --- a/tests/components/homekit/test_get_accessories.py +++ b/tests/components/homekit/test_get_accessories.py @@ -10,7 +10,8 @@ from homeassistant.components.homekit import get_accessory, TYPES from homeassistant.const import ( ATTR_CODE, ATTR_DEVICE_CLASS, ATTR_SUPPORTED_FEATURES, - ATTR_UNIT_OF_MEASUREMENT, CONF_NAME, TEMP_CELSIUS, TEMP_FAHRENHEIT) + ATTR_UNIT_OF_MEASUREMENT, CONF_MODE, CONF_NAME, TEMP_CELSIUS, + TEMP_FAHRENHEIT) def test_not_supported(caplog): @@ -38,10 +39,16 @@ def test_customize_options(config, name): @pytest.mark.parametrize('type_name, entity_id, state, attrs, config', [ ('Fan', 'fan.test', 'on', {}, {}), + ('Light', 'light.test', 'on', {}, {}), + ('Lock', 'lock.test', 'locked', {}, {ATTR_CODE: '1234'}), + + ('MediaPlayer', 'media_player.test', 'on', {}, {CONF_MODE: 'on_off'}), + ('SecuritySystem', 'alarm_control_panel.test', 'armed', {}, {ATTR_CODE: '1234'}), + ('Thermostat', 'climate.test', 'auto', {}, {}), ('Thermostat', 'climate.test', 'auto', {ATTR_SUPPORTED_FEATURES: SUPPORT_TARGET_TEMPERATURE_LOW | diff --git a/tests/components/homekit/test_type_media_players.py b/tests/components/homekit/test_type_media_players.py new file mode 100644 index 0000000000000..63fb99d36328a --- /dev/null +++ b/tests/components/homekit/test_type_media_players.py @@ -0,0 +1,105 @@ +"""Test different accessory types: Media Players.""" + +from homeassistant.components.media_player import ( + ATTR_MEDIA_VOLUME_MUTED, DOMAIN) +from homeassistant.components.homekit.type_media_players import (MediaPlayer) +from homeassistant.const import ( + ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, CONF_MODE, SERVICE_MEDIA_PAUSE, + SERVICE_MEDIA_PLAY, SERVICE_MEDIA_STOP, SERVICE_TURN_OFF, SERVICE_TURN_ON, + SERVICE_VOLUME_MUTE, STATE_OFF, STATE_ON, STATE_IDLE, STATE_PAUSED, + STATE_PLAYING) +from homeassistant.components.homekit.const import ( + ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE) +from tests.common import async_mock_service + + +async def test_media_player_set_state(hass): + """Test if accessory and HA are updated accordingly.""" + config = {CONF_MODE: [ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE]} + entity_id = 'media_player.test' + + hass.states.async_set(entity_id, None, {ATTR_SUPPORTED_FEATURES: 20873, + ATTR_MEDIA_VOLUME_MUTED: False}) + await hass.async_block_till_done() + acc = MediaPlayer(hass, 'MediaPlayer', entity_id, 2, config) + await hass.async_add_job(acc.run) + + assert acc.aid == 2 + assert acc.category == 8 # Switch + + assert acc.chars[ON_OFF].value == 0 + assert acc.chars[PLAY_PAUSE].value == 0 + assert acc.chars[PLAY_STOP].value == 0 + assert acc.chars[TOGGLE_MUTE].value == 0 + + hass.states.async_set(entity_id, STATE_ON, {ATTR_MEDIA_VOLUME_MUTED: True}) + await hass.async_block_till_done() + assert acc.chars[ON_OFF].value == 1 + assert acc.chars[TOGGLE_MUTE].value == 1 + + hass.states.async_set(entity_id, STATE_OFF) + await hass.async_block_till_done() + assert acc.chars[ON_OFF].value == 0 + + hass.states.async_set(entity_id, STATE_PLAYING) + await hass.async_block_till_done() + assert acc.chars[PLAY_PAUSE].value == 1 + assert acc.chars[PLAY_STOP].value == 1 + + hass.states.async_set(entity_id, STATE_PAUSED) + await hass.async_block_till_done() + assert acc.chars[PLAY_PAUSE].value == 0 + + hass.states.async_set(entity_id, STATE_IDLE) + await hass.async_block_till_done() + assert acc.chars[PLAY_STOP].value == 0 + + # Set from HomeKit + call_turn_on = async_mock_service(hass, DOMAIN, SERVICE_TURN_ON) + call_turn_off = async_mock_service(hass, DOMAIN, SERVICE_TURN_OFF) + call_media_play = async_mock_service(hass, DOMAIN, SERVICE_MEDIA_PLAY) + call_media_pause = async_mock_service(hass, DOMAIN, SERVICE_MEDIA_PAUSE) + call_media_stop = async_mock_service(hass, DOMAIN, SERVICE_MEDIA_STOP) + call_toggle_mute = async_mock_service(hass, DOMAIN, SERVICE_VOLUME_MUTE) + + await hass.async_add_job(acc.chars[ON_OFF].client_update_value, True) + await hass.async_block_till_done() + assert call_turn_on + assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id + + await hass.async_add_job(acc.chars[ON_OFF].client_update_value, False) + await hass.async_block_till_done() + assert call_turn_off + assert call_turn_off[0].data[ATTR_ENTITY_ID] == entity_id + + await hass.async_add_job(acc.chars[PLAY_PAUSE].client_update_value, True) + await hass.async_block_till_done() + assert call_media_play + assert call_media_play[0].data[ATTR_ENTITY_ID] == entity_id + + await hass.async_add_job(acc.chars[PLAY_PAUSE].client_update_value, False) + await hass.async_block_till_done() + assert call_media_pause + assert call_media_pause[0].data[ATTR_ENTITY_ID] == entity_id + + await hass.async_add_job(acc.chars[PLAY_STOP].client_update_value, True) + await hass.async_block_till_done() + assert call_media_play + assert call_media_play[1].data[ATTR_ENTITY_ID] == entity_id + + await hass.async_add_job(acc.chars[PLAY_STOP].client_update_value, False) + await hass.async_block_till_done() + assert call_media_stop + assert call_media_stop[0].data[ATTR_ENTITY_ID] == entity_id + + await hass.async_add_job(acc.chars[TOGGLE_MUTE].client_update_value, True) + await hass.async_block_till_done() + assert call_toggle_mute + assert call_toggle_mute[0].data[ATTR_ENTITY_ID] == entity_id + assert call_toggle_mute[0].data[ATTR_MEDIA_VOLUME_MUTED] is True + + await hass.async_add_job(acc.chars[TOGGLE_MUTE].client_update_value, False) + await hass.async_block_till_done() + assert call_toggle_mute + assert call_toggle_mute[1].data[ATTR_ENTITY_ID] == entity_id + assert call_toggle_mute[1].data[ATTR_MEDIA_VOLUME_MUTED] is False diff --git a/tests/components/homekit/test_util.py b/tests/components/homekit/test_util.py index 0755e8f54d455..518a4b79b715f 100644 --- a/tests/components/homekit/test_util.py +++ b/tests/components/homekit/test_util.py @@ -2,16 +2,19 @@ import pytest import voluptuous as vol +from homeassistant.core import State from homeassistant.components.homekit.const import HOMEKIT_NOTIFY_ID from homeassistant.components.homekit.util import ( convert_to_float, density_to_air_quality, dismiss_setup_message, - show_setup_message, temperature_to_homekit, temperature_to_states) + show_setup_message, temperature_to_homekit, temperature_to_states, + validate_media_player_modes) from homeassistant.components.homekit.util import validate_entity_config \ as vec from homeassistant.components.persistent_notification import ( ATTR_MESSAGE, ATTR_NOTIFICATION_ID, DOMAIN) from homeassistant.const import ( - ATTR_CODE, CONF_NAME, STATE_UNKNOWN, TEMP_CELSIUS, TEMP_FAHRENHEIT) + ATTR_CODE, ATTR_SUPPORTED_FEATURES, CONF_MODE, CONF_NAME, STATE_UNKNOWN, + TEMP_CELSIUS, TEMP_FAHRENHEIT) from tests.common import async_mock_service @@ -20,7 +23,8 @@ def test_validate_entity_config(): """Test validate entities.""" configs = [{'invalid_entity_id': {}}, {'demo.test': 1}, {'demo.test': 'test'}, {'demo.test': [1, 2]}, - {'demo.test': None}, {'demo.test': {CONF_NAME: None}}] + {'demo.test': None}, {'demo.test': {CONF_NAME: None}}, + {'media_player.test': {CONF_MODE: 'on_on'}}] for conf in configs: with pytest.raises(vol.Invalid): @@ -39,6 +43,22 @@ def test_validate_entity_config(): assert vec({'lock.demo': {ATTR_CODE: '1234'}}) == \ {'lock.demo': {ATTR_CODE: '1234'}} + assert vec({'media_player.demo': {}}) == {'media_player.demo': {}} + assert vec({'media_player.demo': {CONF_MODE: 'on_off'}}) == \ + {'media_player.demo': {CONF_MODE: ['on_off']}} + + +@pytest.mark.parametrize('config, attrs, validated_modes', [ + ({}, {ATTR_SUPPORTED_FEATURES: 20873}, + ['on_off', 'play_pause', 'play_stop', 'toggle_mute']), + ({CONF_MODE: ['on_off', 'play_pause']}, {ATTR_SUPPORTED_FEATURES: 384}, + ['on_off']), +]) +def test_validate_media_player_modes(config, attrs, validated_modes): + """Test validating modes for media playeres.""" + entity_state = State('media_player.demo', 'on', attrs) + assert validate_media_player_modes(entity_state, config) == validated_modes + def test_convert_to_float(): """Test convert_to_float method.""" From 757727a23d680e2e055fca1b2f2403af90bc4f3c Mon Sep 17 00:00:00 2001 From: Matt Schmitt Date: Mon, 21 May 2018 07:51:13 -0400 Subject: [PATCH 4/7] Style clean up and update tests --- homeassistant/components/homekit/__init__.py | 5 ++-- .../components/homekit/type_media_players.py | 12 +++----- homeassistant/components/homekit/util.py | 28 ++++++++---------- .../homekit/test_get_accessories.py | 18 ++++++++---- .../homekit/test_type_media_players.py | 5 ++-- tests/components/homekit/test_util.py | 29 ++++++++++--------- 6 files changed, 49 insertions(+), 48 deletions(-) diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index b8a028146657e..01d4f23774c46 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -127,9 +127,8 @@ def get_accessory(hass, state, aid, config): a_type = 'Lock' elif state.domain == 'media_player': - config[CONF_MODE] = validate_media_player_modes(state, config) - if config[CONF_MODE] is not None: - a_type = 'MediaPlayer' + validate_media_player_modes(state, config) + a_type = 'MediaPlayer' elif state.domain == 'sensor': unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) diff --git a/homeassistant/components/homekit/type_media_players.py b/homeassistant/components/homekit/type_media_players.py index caf8cce2b8c77..563cd0cb25cfa 100644 --- a/homeassistant/components/homekit/type_media_players.py +++ b/homeassistant/components/homekit/type_media_players.py @@ -33,11 +33,9 @@ def __init__(self, *args): super().__init__(*args, category=CATEGORY_SWITCH) self._flag = {ON_OFF: False, PLAY_PAUSE: False, PLAY_STOP: False, TOGGLE_MUTE: False} - self.chars = {ON_OFF: None, PLAY_PAUSE: None, PLAY_STOP: None, TOGGLE_MUTE: None} - - modes = self.config.get(CONF_MODE) + modes = self.config[CONF_MODE] if ON_OFF in modes: serv_on_off = self.add_preload_service(SERV_SWITCH, CHAR_NAME) @@ -112,8 +110,7 @@ def update_state(self, new_state): current_state = new_state.state if self.chars[ON_OFF]: - hk_state = current_state \ - not in (STATE_OFF, STATE_UNKNOWN, 'None') + hk_state = current_state not in (STATE_OFF, STATE_UNKNOWN, 'None') if not self._flag[ON_OFF]: _LOGGER.debug('%s: Set current state for "on_off" to %s', self.entity_id, hk_state) @@ -138,9 +135,8 @@ def update_state(self, new_state): if self.chars[TOGGLE_MUTE]: current_state = new_state.attributes.get(ATTR_MEDIA_VOLUME_MUTED) - hk_state = current_state if not self._flag[TOGGLE_MUTE]: _LOGGER.debug('%s: Set current state for "toggle_mute" to %s', - self.entity_id, hk_state) - self.chars[TOGGLE_MUTE].set_value(hk_state) + self.entity_id, current_state) + self.chars[TOGGLE_MUTE].set_value(current_state) self._flag[TOGGLE_MUTE] = False diff --git a/homeassistant/components/homekit/util.py b/homeassistant/components/homekit/util.py index 08dfcf0462689..43c63ebae7416 100644 --- a/homeassistant/components/homekit/util.py +++ b/homeassistant/components/homekit/util.py @@ -42,13 +42,12 @@ def validate_entity_config(values): if domain == 'media_player': mode = config.get(CONF_MODE) - if mode: - params[CONF_MODE] = cv.ensure_list(mode) - for mode in params[CONF_MODE]: - if mode not in MEDIA_PLAYER_MODES: - raise vol.Invalid( - 'Invalid mode: "{}", valid modes are: "{}".' - .format(mode, MEDIA_PLAYER_MODES)) + params[CONF_MODE] = cv.ensure_list(mode) + for key in params[CONF_MODE]: + if key not in MEDIA_PLAYER_MODES: + raise vol.Invalid( + 'Invalid mode: "{}", valid modes are: "{}".' + .format(key, MEDIA_PLAYER_MODES)) entities[entity] = params return entities @@ -68,17 +67,14 @@ def validate_media_player_modes(state, config): if features & SUPPORT_VOLUME_MUTE: supported_modes.append(TOGGLE_MUTE) - config_modes = config.get(CONF_MODE, MEDIA_PLAYER_MODES) + if not config[CONF_MODE]: + config[CONF_MODE] = supported_modes + return - validated_modes = [] - for mode in config_modes: - if mode in supported_modes and mode not in validated_modes: - validated_modes.append(mode) + for mode in config[CONF_MODE]: if mode not in supported_modes: - _LOGGER.warning('The entity "%s" does not support ' - 'mode: "%s", supported modes are: %s', - state.entity_id, mode, supported_modes) - return validated_modes + raise vol.Invalid('"{}" does not support mode: "{}".' + .format(state.entity_id, mode)) def show_setup_message(hass, pincode): diff --git a/tests/components/homekit/test_get_accessories.py b/tests/components/homekit/test_get_accessories.py index d4d769a419197..3d6f7c7ff151a 100644 --- a/tests/components/homekit/test_get_accessories.py +++ b/tests/components/homekit/test_get_accessories.py @@ -2,12 +2,16 @@ from unittest.mock import patch, Mock import pytest +import voluptuous as vol from homeassistant.core import State from homeassistant.components.cover import SUPPORT_CLOSE, SUPPORT_OPEN from homeassistant.components.climate import ( SUPPORT_TARGET_TEMPERATURE_HIGH, SUPPORT_TARGET_TEMPERATURE_LOW) +from homeassistant.components.media_player import ( + SUPPORT_PAUSE, SUPPORT_SEEK, SUPPORT_TURN_OFF, SUPPORT_TURN_ON) from homeassistant.components.homekit import get_accessory, TYPES +from homeassistant.components.homekit.const import ON_OFF from homeassistant.const import ( ATTR_CODE, ATTR_DEVICE_CLASS, ATTR_SUPPORTED_FEATURES, ATTR_UNIT_OF_MEASUREMENT, CONF_MODE, CONF_NAME, TEMP_CELSIUS, @@ -24,6 +28,11 @@ def test_not_supported(caplog): assert caplog.records[0].levelname == 'WARNING' assert 'invalid aid' in caplog.records[0].msg + with pytest.raises(vol.Invalid): + attrs = {ATTR_SUPPORTED_FEATURES: SUPPORT_PAUSE | SUPPORT_SEEK} + entity_state = State('media_player.demo', 'on', attrs) + get_accessory(None, entity_state, 2, {CONF_MODE: [ON_OFF]}) + @pytest.mark.parametrize('config, name', [ ({CONF_NAME: 'Customize Name'}, 'Customize Name'), @@ -39,16 +48,13 @@ def test_customize_options(config, name): @pytest.mark.parametrize('type_name, entity_id, state, attrs, config', [ ('Fan', 'fan.test', 'on', {}, {}), - ('Light', 'light.test', 'on', {}, {}), - ('Lock', 'lock.test', 'locked', {}, {ATTR_CODE: '1234'}), - - ('MediaPlayer', 'media_player.test', 'on', {}, {CONF_MODE: 'on_off'}), - + ('MediaPlayer', 'media_player.test', 'on', + {ATTR_SUPPORTED_FEATURES: SUPPORT_TURN_ON | SUPPORT_TURN_OFF}, + {CONF_MODE: [ON_OFF]}), ('SecuritySystem', 'alarm_control_panel.test', 'armed', {}, {ATTR_CODE: '1234'}), - ('Thermostat', 'climate.test', 'auto', {}, {}), ('Thermostat', 'climate.test', 'auto', {ATTR_SUPPORTED_FEATURES: SUPPORT_TARGET_TEMPERATURE_LOW | diff --git a/tests/components/homekit/test_type_media_players.py b/tests/components/homekit/test_type_media_players.py index 63fb99d36328a..01b3490fe9b6c 100644 --- a/tests/components/homekit/test_type_media_players.py +++ b/tests/components/homekit/test_type_media_players.py @@ -3,13 +3,14 @@ from homeassistant.components.media_player import ( ATTR_MEDIA_VOLUME_MUTED, DOMAIN) from homeassistant.components.homekit.type_media_players import (MediaPlayer) +from homeassistant.components.homekit.const import ( + ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE) from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, CONF_MODE, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_STOP, SERVICE_TURN_OFF, SERVICE_TURN_ON, SERVICE_VOLUME_MUTE, STATE_OFF, STATE_ON, STATE_IDLE, STATE_PAUSED, STATE_PLAYING) -from homeassistant.components.homekit.const import ( - ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE) + from tests.common import async_mock_service diff --git a/tests/components/homekit/test_util.py b/tests/components/homekit/test_util.py index 518a4b79b715f..492fd0e21b8ab 100644 --- a/tests/components/homekit/test_util.py +++ b/tests/components/homekit/test_util.py @@ -3,7 +3,8 @@ import voluptuous as vol from homeassistant.core import State -from homeassistant.components.homekit.const import HOMEKIT_NOTIFY_ID +from homeassistant.components.homekit.const import ( + HOMEKIT_NOTIFY_ID, ON_OFF, PLAY_PAUSE) from homeassistant.components.homekit.util import ( convert_to_float, density_to_air_quality, dismiss_setup_message, show_setup_message, temperature_to_homekit, temperature_to_states, @@ -43,21 +44,23 @@ def test_validate_entity_config(): assert vec({'lock.demo': {ATTR_CODE: '1234'}}) == \ {'lock.demo': {ATTR_CODE: '1234'}} - assert vec({'media_player.demo': {}}) == {'media_player.demo': {}} - assert vec({'media_player.demo': {CONF_MODE: 'on_off'}}) == \ - {'media_player.demo': {CONF_MODE: ['on_off']}} + assert vec({'media_player.demo': {}}) == \ + {'media_player.demo': {CONF_MODE: []}} + assert vec({'media_player.demo': {CONF_MODE: [ON_OFF]}}) == \ + {'media_player.demo': {CONF_MODE: [ON_OFF]}} -@pytest.mark.parametrize('config, attrs, validated_modes', [ - ({}, {ATTR_SUPPORTED_FEATURES: 20873}, - ['on_off', 'play_pause', 'play_stop', 'toggle_mute']), - ({CONF_MODE: ['on_off', 'play_pause']}, {ATTR_SUPPORTED_FEATURES: 384}, - ['on_off']), -]) -def test_validate_media_player_modes(config, attrs, validated_modes): - """Test validating modes for media playeres.""" +def test_validate_media_player_modes(): + """Test validate modes for media playeres.""" + attrs = {ATTR_SUPPORTED_FEATURES: 20873} entity_state = State('media_player.demo', 'on', attrs) - assert validate_media_player_modes(entity_state, config) == validated_modes + validate_media_player_modes(entity_state, {CONF_MODE: []}) + + attrs = {ATTR_SUPPORTED_FEATURES: 384} + entity_state = State('media_player.demo', 'on', attrs) + config = {CONF_MODE: [ON_OFF, PLAY_PAUSE]} + with pytest.raises(vol.Invalid): + validate_media_player_modes(entity_state, config) def test_convert_to_float(): From e905a6e91c63fc3da5df1d7ce3ea6dc3440af9c6 Mon Sep 17 00:00:00 2001 From: Matt Schmitt Date: Mon, 21 May 2018 09:58:00 -0400 Subject: [PATCH 5/7] Update tests --- homeassistant/components/homekit/__init__.py | 3 ++- homeassistant/components/homekit/util.py | 2 +- tests/components/homekit/test_get_accessories.py | 8 ++++++++ tests/components/homekit/test_util.py | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index 01d4f23774c46..f7bb5ec38c555 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -128,7 +128,8 @@ def get_accessory(hass, state, aid, config): elif state.domain == 'media_player': validate_media_player_modes(state, config) - a_type = 'MediaPlayer' + if config.get(CONF_MODE): + a_type = 'MediaPlayer' elif state.domain == 'sensor': unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) diff --git a/homeassistant/components/homekit/util.py b/homeassistant/components/homekit/util.py index 43c63ebae7416..57ce562ce215d 100644 --- a/homeassistant/components/homekit/util.py +++ b/homeassistant/components/homekit/util.py @@ -67,7 +67,7 @@ def validate_media_player_modes(state, config): if features & SUPPORT_VOLUME_MUTE: supported_modes.append(TOGGLE_MUTE) - if not config[CONF_MODE]: + if not config.get(CONF_MODE): config[CONF_MODE] = supported_modes return diff --git a/tests/components/homekit/test_get_accessories.py b/tests/components/homekit/test_get_accessories.py index 3d6f7c7ff151a..a5e0aa10c73c7 100644 --- a/tests/components/homekit/test_get_accessories.py +++ b/tests/components/homekit/test_get_accessories.py @@ -28,11 +28,19 @@ def test_not_supported(caplog): assert caplog.records[0].levelname == 'WARNING' assert 'invalid aid' in caplog.records[0].msg + +def test_not_supported_media_player(): + """Test if mode isn't supported and if no supported modes.""" + # selected mode for entity not supported with pytest.raises(vol.Invalid): attrs = {ATTR_SUPPORTED_FEATURES: SUPPORT_PAUSE | SUPPORT_SEEK} entity_state = State('media_player.demo', 'on', attrs) get_accessory(None, entity_state, 2, {CONF_MODE: [ON_OFF]}) + # no supported modes for entity + entity_state = State('media_player.demo', 'on') + assert get_accessory(None, entity_state, 2, {}) is None + @pytest.mark.parametrize('config, name', [ ({CONF_NAME: 'Customize Name'}, 'Customize Name'), diff --git a/tests/components/homekit/test_util.py b/tests/components/homekit/test_util.py index 492fd0e21b8ab..064162eece586 100644 --- a/tests/components/homekit/test_util.py +++ b/tests/components/homekit/test_util.py @@ -54,7 +54,7 @@ def test_validate_media_player_modes(): """Test validate modes for media playeres.""" attrs = {ATTR_SUPPORTED_FEATURES: 20873} entity_state = State('media_player.demo', 'on', attrs) - validate_media_player_modes(entity_state, {CONF_MODE: []}) + validate_media_player_modes(entity_state, {}) attrs = {ATTR_SUPPORTED_FEATURES: 384} entity_state = State('media_player.demo', 'on', attrs) From 63d8d7a1493f89e43ba5aa9581f0e4d762841a92 Mon Sep 17 00:00:00 2001 From: Matt Schmitt Date: Mon, 21 May 2018 10:11:39 -0400 Subject: [PATCH 6/7] Fix error after rebase --- homeassistant/components/homekit/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index f7bb5ec38c555..561301cdb6d13 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -24,7 +24,7 @@ from .const import ( CONF_AUTO_START, CONF_ENTITY_CONFIG, CONF_FILTER, DEFAULT_AUTO_START, DEFAULT_PORT, DEVICE_CLASS_CO2, DEVICE_CLASS_PM25, DOMAIN, HOMEKIT_FILE, - ON_OFF, PLAY_PAUSE, PLAY_STOP, SERVICE_HOMEKIT_START, TOGGLE_MUTE) + SERVICE_HOMEKIT_START) from .util import ( show_setup_message, validate_entity_config, validate_media_player_modes) From ce068bec5392489ddbc3878d28735c7f9176ca68 Mon Sep 17 00:00:00 2001 From: Matt Schmitt Date: Mon, 21 May 2018 23:00:59 -0400 Subject: [PATCH 7/7] Minor style and test fixes --- tests/components/homekit/test_get_accessories.py | 5 ++--- .../components/homekit/test_type_media_players.py | 4 ++-- tests/components/homekit/test_util.py | 15 ++++++++------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/components/homekit/test_get_accessories.py b/tests/components/homekit/test_get_accessories.py index a5e0aa10c73c7..6f6d39e477ab7 100644 --- a/tests/components/homekit/test_get_accessories.py +++ b/tests/components/homekit/test_get_accessories.py @@ -9,7 +9,7 @@ from homeassistant.components.climate import ( SUPPORT_TARGET_TEMPERATURE_HIGH, SUPPORT_TARGET_TEMPERATURE_LOW) from homeassistant.components.media_player import ( - SUPPORT_PAUSE, SUPPORT_SEEK, SUPPORT_TURN_OFF, SUPPORT_TURN_ON) + SUPPORT_TURN_OFF, SUPPORT_TURN_ON) from homeassistant.components.homekit import get_accessory, TYPES from homeassistant.components.homekit.const import ON_OFF from homeassistant.const import ( @@ -33,8 +33,7 @@ def test_not_supported_media_player(): """Test if mode isn't supported and if no supported modes.""" # selected mode for entity not supported with pytest.raises(vol.Invalid): - attrs = {ATTR_SUPPORTED_FEATURES: SUPPORT_PAUSE | SUPPORT_SEEK} - entity_state = State('media_player.demo', 'on', attrs) + entity_state = State('media_player.demo', 'on') get_accessory(None, entity_state, 2, {CONF_MODE: [ON_OFF]}) # no supported modes for entity diff --git a/tests/components/homekit/test_type_media_players.py b/tests/components/homekit/test_type_media_players.py index 01b3490fe9b6c..03135b1418ecf 100644 --- a/tests/components/homekit/test_type_media_players.py +++ b/tests/components/homekit/test_type_media_players.py @@ -2,13 +2,13 @@ from homeassistant.components.media_player import ( ATTR_MEDIA_VOLUME_MUTED, DOMAIN) -from homeassistant.components.homekit.type_media_players import (MediaPlayer) +from homeassistant.components.homekit.type_media_players import MediaPlayer from homeassistant.components.homekit.const import ( ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE) from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, CONF_MODE, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_STOP, SERVICE_TURN_OFF, SERVICE_TURN_ON, - SERVICE_VOLUME_MUTE, STATE_OFF, STATE_ON, STATE_IDLE, STATE_PAUSED, + SERVICE_VOLUME_MUTE, STATE_IDLE, STATE_OFF, STATE_ON, STATE_PAUSED, STATE_PLAYING) from tests.common import async_mock_service diff --git a/tests/components/homekit/test_util.py b/tests/components/homekit/test_util.py index 064162eece586..56a625e02d7f1 100644 --- a/tests/components/homekit/test_util.py +++ b/tests/components/homekit/test_util.py @@ -4,7 +4,7 @@ from homeassistant.core import State from homeassistant.components.homekit.const import ( - HOMEKIT_NOTIFY_ID, ON_OFF, PLAY_PAUSE) + HOMEKIT_NOTIFY_ID, ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE) from homeassistant.components.homekit.util import ( convert_to_float, density_to_air_quality, dismiss_setup_message, show_setup_message, temperature_to_homekit, temperature_to_states, @@ -25,7 +25,7 @@ def test_validate_entity_config(): configs = [{'invalid_entity_id': {}}, {'demo.test': 1}, {'demo.test': 'test'}, {'demo.test': [1, 2]}, {'demo.test': None}, {'demo.test': {CONF_NAME: None}}, - {'media_player.test': {CONF_MODE: 'on_on'}}] + {'media_player.test': {CONF_MODE: 'invalid_mode'}}] for conf in configs: with pytest.raises(vol.Invalid): @@ -51,14 +51,15 @@ def test_validate_entity_config(): def test_validate_media_player_modes(): - """Test validate modes for media playeres.""" + """Test validate modes for media players.""" + config = {} attrs = {ATTR_SUPPORTED_FEATURES: 20873} entity_state = State('media_player.demo', 'on', attrs) - validate_media_player_modes(entity_state, {}) + validate_media_player_modes(entity_state, config) + assert config == {CONF_MODE: [ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE]} - attrs = {ATTR_SUPPORTED_FEATURES: 384} - entity_state = State('media_player.demo', 'on', attrs) - config = {CONF_MODE: [ON_OFF, PLAY_PAUSE]} + entity_state = State('media_player.demo', 'on') + config = {CONF_MODE: [ON_OFF]} with pytest.raises(vol.Invalid): validate_media_player_modes(entity_state, config)