mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Improve Homekit media_player options (#14637)
* Optimize imports * Optimize name * Optimize config schema * Rename mode to feature * Replace mode with feature_list
This commit is contained in:
parent
27f3285d17
commit
6f4dd7b057
@ -9,12 +9,11 @@ from zlib import adler32
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.cover import (
|
import homeassistant.components.cover as cover
|
||||||
SUPPORT_CLOSE, SUPPORT_OPEN, SUPPORT_SET_POSITION)
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_DEVICE_CLASS, ATTR_SUPPORTED_FEATURES, ATTR_UNIT_OF_MEASUREMENT,
|
ATTR_DEVICE_CLASS, ATTR_SUPPORTED_FEATURES, ATTR_UNIT_OF_MEASUREMENT,
|
||||||
CONF_IP_ADDRESS, CONF_MODE, CONF_NAME, CONF_PORT,
|
CONF_IP_ADDRESS, CONF_NAME, CONF_PORT, DEVICE_CLASS_HUMIDITY,
|
||||||
DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_TEMPERATURE,
|
DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_TEMPERATURE,
|
||||||
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
|
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP,
|
||||||
TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
@ -22,11 +21,11 @@ from homeassistant.helpers.entityfilter import FILTER_SCHEMA
|
|||||||
from homeassistant.util import get_local_ip
|
from homeassistant.util import get_local_ip
|
||||||
from homeassistant.util.decorator import Registry
|
from homeassistant.util.decorator import Registry
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_AUTO_START, CONF_ENTITY_CONFIG, CONF_FILTER, DEFAULT_AUTO_START,
|
CONF_AUTO_START, CONF_ENTITY_CONFIG, CONF_FEATURE_LIST, CONF_FILTER,
|
||||||
DEFAULT_PORT, DEVICE_CLASS_CO2, DEVICE_CLASS_PM25, DOMAIN, HOMEKIT_FILE,
|
DEFAULT_AUTO_START, DEFAULT_PORT, DEVICE_CLASS_CO2, DEVICE_CLASS_PM25,
|
||||||
SERVICE_HOMEKIT_START)
|
DOMAIN, HOMEKIT_FILE, SERVICE_HOMEKIT_START)
|
||||||
from .util import (
|
from .util import (
|
||||||
show_setup_message, validate_entity_config, validate_media_player_modes)
|
show_setup_message, validate_entity_config, validate_media_player_features)
|
||||||
|
|
||||||
TYPES = Registry()
|
TYPES = Registry()
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -110,11 +109,11 @@ def get_accessory(hass, state, aid, config):
|
|||||||
device_class = state.attributes.get(ATTR_DEVICE_CLASS)
|
device_class = state.attributes.get(ATTR_DEVICE_CLASS)
|
||||||
|
|
||||||
if device_class == 'garage' and \
|
if device_class == 'garage' and \
|
||||||
features & (SUPPORT_OPEN | SUPPORT_CLOSE):
|
features & (cover.SUPPORT_OPEN | cover.SUPPORT_CLOSE):
|
||||||
a_type = 'GarageDoorOpener'
|
a_type = 'GarageDoorOpener'
|
||||||
elif features & SUPPORT_SET_POSITION:
|
elif features & cover.SUPPORT_SET_POSITION:
|
||||||
a_type = 'WindowCovering'
|
a_type = 'WindowCovering'
|
||||||
elif features & (SUPPORT_OPEN | SUPPORT_CLOSE):
|
elif features & (cover.SUPPORT_OPEN | cover.SUPPORT_CLOSE):
|
||||||
a_type = 'WindowCoveringBasic'
|
a_type = 'WindowCoveringBasic'
|
||||||
|
|
||||||
elif state.domain == 'fan':
|
elif state.domain == 'fan':
|
||||||
@ -127,8 +126,9 @@ def get_accessory(hass, state, aid, config):
|
|||||||
a_type = 'Lock'
|
a_type = 'Lock'
|
||||||
|
|
||||||
elif state.domain == 'media_player':
|
elif state.domain == 'media_player':
|
||||||
validate_media_player_modes(state, config)
|
feature_list = config.get(CONF_FEATURE_LIST)
|
||||||
if config.get(CONF_MODE):
|
if feature_list and \
|
||||||
|
validate_media_player_features(state, feature_list):
|
||||||
a_type = 'MediaPlayer'
|
a_type = 'MediaPlayer'
|
||||||
|
|
||||||
elif state.domain == 'sensor':
|
elif state.domain == 'sensor':
|
||||||
|
@ -8,12 +8,20 @@ HOMEKIT_NOTIFY_ID = 4663548
|
|||||||
# #### Config ####
|
# #### Config ####
|
||||||
CONF_AUTO_START = 'auto_start'
|
CONF_AUTO_START = 'auto_start'
|
||||||
CONF_ENTITY_CONFIG = 'entity_config'
|
CONF_ENTITY_CONFIG = 'entity_config'
|
||||||
|
CONF_FEATURE = 'feature'
|
||||||
|
CONF_FEATURE_LIST = 'feature_list'
|
||||||
CONF_FILTER = 'filter'
|
CONF_FILTER = 'filter'
|
||||||
|
|
||||||
# #### Config Defaults ####
|
# #### Config Defaults ####
|
||||||
DEFAULT_AUTO_START = True
|
DEFAULT_AUTO_START = True
|
||||||
DEFAULT_PORT = 51827
|
DEFAULT_PORT = 51827
|
||||||
|
|
||||||
|
# #### Features ####
|
||||||
|
FEATURE_ON_OFF = 'on_off'
|
||||||
|
FEATURE_PLAY_PAUSE = 'play_pause'
|
||||||
|
FEATURE_PLAY_STOP = 'play_stop'
|
||||||
|
FEATURE_TOGGLE_MUTE = 'toggle_mute'
|
||||||
|
|
||||||
# #### HomeKit Component Services ####
|
# #### HomeKit Component Services ####
|
||||||
SERVICE_HOMEKIT_START = 'start'
|
SERVICE_HOMEKIT_START = 'start'
|
||||||
|
|
||||||
@ -23,12 +31,6 @@ BRIDGE_NAME = 'Home Assistant Bridge'
|
|||||||
BRIDGE_SERIAL_NUMBER = 'homekit.bridge'
|
BRIDGE_SERIAL_NUMBER = 'homekit.bridge'
|
||||||
MANUFACTURER = 'Home Assistant'
|
MANUFACTURER = 'Home Assistant'
|
||||||
|
|
||||||
# #### Media Player Modes ####
|
|
||||||
ON_OFF = 'on_off'
|
|
||||||
PLAY_PAUSE = 'play_pause'
|
|
||||||
PLAY_STOP = 'play_stop'
|
|
||||||
TOGGLE_MUTE = 'toggle_mute'
|
|
||||||
|
|
||||||
# #### Services ####
|
# #### Services ####
|
||||||
SERV_ACCESSORY_INFO = 'AccessoryInformation'
|
SERV_ACCESSORY_INFO = 'AccessoryInformation'
|
||||||
SERV_AIR_QUALITY_SENSOR = 'AirQualitySensor'
|
SERV_AIR_QUALITY_SENSOR = 'AirQualitySensor'
|
||||||
|
@ -4,7 +4,7 @@ import logging
|
|||||||
from pyhap.const import CATEGORY_SWITCH
|
from pyhap.const import CATEGORY_SWITCH
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID, CONF_MODE, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY,
|
ATTR_ENTITY_ID, SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY,
|
||||||
SERVICE_MEDIA_STOP, SERVICE_TURN_OFF, SERVICE_TURN_ON, SERVICE_VOLUME_MUTE,
|
SERVICE_MEDIA_STOP, SERVICE_TURN_OFF, SERVICE_TURN_ON, SERVICE_VOLUME_MUTE,
|
||||||
STATE_OFF, STATE_PLAYING, STATE_UNKNOWN)
|
STATE_OFF, STATE_PLAYING, STATE_UNKNOWN)
|
||||||
from homeassistant.components.media_player import (
|
from homeassistant.components.media_player import (
|
||||||
@ -13,15 +13,15 @@ from homeassistant.components.media_player import (
|
|||||||
from . import TYPES
|
from . import TYPES
|
||||||
from .accessories import HomeAccessory
|
from .accessories import HomeAccessory
|
||||||
from .const import (
|
from .const import (
|
||||||
CHAR_NAME, CHAR_ON, ON_OFF, PLAY_PAUSE, PLAY_STOP, SERV_SWITCH,
|
CHAR_NAME, CHAR_ON, CONF_FEATURE_LIST, FEATURE_ON_OFF, FEATURE_PLAY_PAUSE,
|
||||||
TOGGLE_MUTE)
|
FEATURE_PLAY_STOP, FEATURE_TOGGLE_MUTE, SERV_SWITCH)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
MODE_FRIENDLY_NAME = {ON_OFF: 'Power',
|
MODE_FRIENDLY_NAME = {FEATURE_ON_OFF: 'Power',
|
||||||
PLAY_PAUSE: 'Play/Pause',
|
FEATURE_PLAY_PAUSE: 'Play/Pause',
|
||||||
PLAY_STOP: 'Play/Stop',
|
FEATURE_PLAY_STOP: 'Play/Stop',
|
||||||
TOGGLE_MUTE: 'Mute'}
|
FEATURE_TOGGLE_MUTE: 'Mute'}
|
||||||
|
|
||||||
|
|
||||||
@TYPES.register('MediaPlayer')
|
@TYPES.register('MediaPlayer')
|
||||||
@ -31,38 +31,38 @@ class MediaPlayer(HomeAccessory):
|
|||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
"""Initialize a Switch accessory object."""
|
"""Initialize a Switch accessory object."""
|
||||||
super().__init__(*args, category=CATEGORY_SWITCH)
|
super().__init__(*args, category=CATEGORY_SWITCH)
|
||||||
self._flag = {ON_OFF: False, PLAY_PAUSE: False,
|
self._flag = {FEATURE_ON_OFF: False, FEATURE_PLAY_PAUSE: False,
|
||||||
PLAY_STOP: False, TOGGLE_MUTE: False}
|
FEATURE_PLAY_STOP: False, FEATURE_TOGGLE_MUTE: False}
|
||||||
self.chars = {ON_OFF: None, PLAY_PAUSE: None,
|
self.chars = {FEATURE_ON_OFF: None, FEATURE_PLAY_PAUSE: None,
|
||||||
PLAY_STOP: None, TOGGLE_MUTE: None}
|
FEATURE_PLAY_STOP: None, FEATURE_TOGGLE_MUTE: None}
|
||||||
modes = self.config[CONF_MODE]
|
feature_list = self.config[CONF_FEATURE_LIST]
|
||||||
|
|
||||||
if ON_OFF in modes:
|
if FEATURE_ON_OFF in feature_list:
|
||||||
|
name = self.generate_service_name(FEATURE_ON_OFF)
|
||||||
serv_on_off = self.add_preload_service(SERV_SWITCH, CHAR_NAME)
|
serv_on_off = self.add_preload_service(SERV_SWITCH, CHAR_NAME)
|
||||||
serv_on_off.configure_char(
|
serv_on_off.configure_char(CHAR_NAME, value=name)
|
||||||
CHAR_NAME, value=self.generate_service_name(ON_OFF))
|
self.chars[FEATURE_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)
|
CHAR_ON, value=False, setter_callback=self.set_on_off)
|
||||||
|
|
||||||
if PLAY_PAUSE in modes:
|
if FEATURE_PLAY_PAUSE in feature_list:
|
||||||
|
name = self.generate_service_name(FEATURE_PLAY_PAUSE)
|
||||||
serv_play_pause = self.add_preload_service(SERV_SWITCH, CHAR_NAME)
|
serv_play_pause = self.add_preload_service(SERV_SWITCH, CHAR_NAME)
|
||||||
serv_play_pause.configure_char(
|
serv_play_pause.configure_char(CHAR_NAME, value=name)
|
||||||
CHAR_NAME, value=self.generate_service_name(PLAY_PAUSE))
|
self.chars[FEATURE_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)
|
CHAR_ON, value=False, setter_callback=self.set_play_pause)
|
||||||
|
|
||||||
if PLAY_STOP in modes:
|
if FEATURE_PLAY_STOP in feature_list:
|
||||||
|
name = self.generate_service_name(FEATURE_PLAY_STOP)
|
||||||
serv_play_stop = self.add_preload_service(SERV_SWITCH, CHAR_NAME)
|
serv_play_stop = self.add_preload_service(SERV_SWITCH, CHAR_NAME)
|
||||||
serv_play_stop.configure_char(
|
serv_play_stop.configure_char(CHAR_NAME, value=name)
|
||||||
CHAR_NAME, value=self.generate_service_name(PLAY_STOP))
|
self.chars[FEATURE_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)
|
CHAR_ON, value=False, setter_callback=self.set_play_stop)
|
||||||
|
|
||||||
if TOGGLE_MUTE in modes:
|
if FEATURE_TOGGLE_MUTE in feature_list:
|
||||||
|
name = self.generate_service_name(FEATURE_TOGGLE_MUTE)
|
||||||
serv_toggle_mute = self.add_preload_service(SERV_SWITCH, CHAR_NAME)
|
serv_toggle_mute = self.add_preload_service(SERV_SWITCH, CHAR_NAME)
|
||||||
serv_toggle_mute.configure_char(
|
serv_toggle_mute.configure_char(CHAR_NAME, value=name)
|
||||||
CHAR_NAME, value=self.generate_service_name(TOGGLE_MUTE))
|
self.chars[FEATURE_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)
|
CHAR_ON, value=False, setter_callback=self.set_toggle_mute)
|
||||||
|
|
||||||
def generate_service_name(self, mode):
|
def generate_service_name(self, mode):
|
||||||
@ -73,7 +73,7 @@ class MediaPlayer(HomeAccessory):
|
|||||||
"""Move switch state to value if call came from HomeKit."""
|
"""Move switch state to value if call came from HomeKit."""
|
||||||
_LOGGER.debug('%s: Set switch state for "on_off" to %s',
|
_LOGGER.debug('%s: Set switch state for "on_off" to %s',
|
||||||
self.entity_id, value)
|
self.entity_id, value)
|
||||||
self._flag[ON_OFF] = True
|
self._flag[FEATURE_ON_OFF] = True
|
||||||
service = SERVICE_TURN_ON if value else SERVICE_TURN_OFF
|
service = SERVICE_TURN_ON if value else SERVICE_TURN_OFF
|
||||||
params = {ATTR_ENTITY_ID: self.entity_id}
|
params = {ATTR_ENTITY_ID: self.entity_id}
|
||||||
self.hass.services.call(DOMAIN, service, params)
|
self.hass.services.call(DOMAIN, service, params)
|
||||||
@ -82,7 +82,7 @@ class MediaPlayer(HomeAccessory):
|
|||||||
"""Move switch state to value if call came from HomeKit."""
|
"""Move switch state to value if call came from HomeKit."""
|
||||||
_LOGGER.debug('%s: Set switch state for "play_pause" to %s',
|
_LOGGER.debug('%s: Set switch state for "play_pause" to %s',
|
||||||
self.entity_id, value)
|
self.entity_id, value)
|
||||||
self._flag[PLAY_PAUSE] = True
|
self._flag[FEATURE_PLAY_PAUSE] = True
|
||||||
service = SERVICE_MEDIA_PLAY if value else SERVICE_MEDIA_PAUSE
|
service = SERVICE_MEDIA_PLAY if value else SERVICE_MEDIA_PAUSE
|
||||||
params = {ATTR_ENTITY_ID: self.entity_id}
|
params = {ATTR_ENTITY_ID: self.entity_id}
|
||||||
self.hass.services.call(DOMAIN, service, params)
|
self.hass.services.call(DOMAIN, service, params)
|
||||||
@ -91,7 +91,7 @@ class MediaPlayer(HomeAccessory):
|
|||||||
"""Move switch state to value if call came from HomeKit."""
|
"""Move switch state to value if call came from HomeKit."""
|
||||||
_LOGGER.debug('%s: Set switch state for "play_stop" to %s',
|
_LOGGER.debug('%s: Set switch state for "play_stop" to %s',
|
||||||
self.entity_id, value)
|
self.entity_id, value)
|
||||||
self._flag[PLAY_STOP] = True
|
self._flag[FEATURE_PLAY_STOP] = True
|
||||||
service = SERVICE_MEDIA_PLAY if value else SERVICE_MEDIA_STOP
|
service = SERVICE_MEDIA_PLAY if value else SERVICE_MEDIA_STOP
|
||||||
params = {ATTR_ENTITY_ID: self.entity_id}
|
params = {ATTR_ENTITY_ID: self.entity_id}
|
||||||
self.hass.services.call(DOMAIN, service, params)
|
self.hass.services.call(DOMAIN, service, params)
|
||||||
@ -100,7 +100,7 @@ class MediaPlayer(HomeAccessory):
|
|||||||
"""Move switch state to value if call came from HomeKit."""
|
"""Move switch state to value if call came from HomeKit."""
|
||||||
_LOGGER.debug('%s: Set switch state for "toggle_mute" to %s',
|
_LOGGER.debug('%s: Set switch state for "toggle_mute" to %s',
|
||||||
self.entity_id, value)
|
self.entity_id, value)
|
||||||
self._flag[TOGGLE_MUTE] = True
|
self._flag[FEATURE_TOGGLE_MUTE] = True
|
||||||
params = {ATTR_ENTITY_ID: self.entity_id,
|
params = {ATTR_ENTITY_ID: self.entity_id,
|
||||||
ATTR_MEDIA_VOLUME_MUTED: value}
|
ATTR_MEDIA_VOLUME_MUTED: value}
|
||||||
self.hass.services.call(DOMAIN, SERVICE_VOLUME_MUTE, params)
|
self.hass.services.call(DOMAIN, SERVICE_VOLUME_MUTE, params)
|
||||||
@ -109,34 +109,34 @@ class MediaPlayer(HomeAccessory):
|
|||||||
"""Update switch state after state changed."""
|
"""Update switch state after state changed."""
|
||||||
current_state = new_state.state
|
current_state = new_state.state
|
||||||
|
|
||||||
if self.chars[ON_OFF]:
|
if self.chars[FEATURE_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]:
|
if not self._flag[FEATURE_ON_OFF]:
|
||||||
_LOGGER.debug('%s: Set current state for "on_off" to %s',
|
_LOGGER.debug('%s: Set current state for "on_off" to %s',
|
||||||
self.entity_id, hk_state)
|
self.entity_id, hk_state)
|
||||||
self.chars[ON_OFF].set_value(hk_state)
|
self.chars[FEATURE_ON_OFF].set_value(hk_state)
|
||||||
self._flag[ON_OFF] = False
|
self._flag[FEATURE_ON_OFF] = False
|
||||||
|
|
||||||
if self.chars[PLAY_PAUSE]:
|
if self.chars[FEATURE_PLAY_PAUSE]:
|
||||||
hk_state = current_state == STATE_PLAYING
|
hk_state = current_state == STATE_PLAYING
|
||||||
if not self._flag[PLAY_PAUSE]:
|
if not self._flag[FEATURE_PLAY_PAUSE]:
|
||||||
_LOGGER.debug('%s: Set current state for "play_pause" to %s',
|
_LOGGER.debug('%s: Set current state for "play_pause" to %s',
|
||||||
self.entity_id, hk_state)
|
self.entity_id, hk_state)
|
||||||
self.chars[PLAY_PAUSE].set_value(hk_state)
|
self.chars[FEATURE_PLAY_PAUSE].set_value(hk_state)
|
||||||
self._flag[PLAY_PAUSE] = False
|
self._flag[FEATURE_PLAY_PAUSE] = False
|
||||||
|
|
||||||
if self.chars[PLAY_STOP]:
|
if self.chars[FEATURE_PLAY_STOP]:
|
||||||
hk_state = current_state == STATE_PLAYING
|
hk_state = current_state == STATE_PLAYING
|
||||||
if not self._flag[PLAY_STOP]:
|
if not self._flag[FEATURE_PLAY_STOP]:
|
||||||
_LOGGER.debug('%s: Set current state for "play_stop" to %s',
|
_LOGGER.debug('%s: Set current state for "play_stop" to %s',
|
||||||
self.entity_id, hk_state)
|
self.entity_id, hk_state)
|
||||||
self.chars[PLAY_STOP].set_value(hk_state)
|
self.chars[FEATURE_PLAY_STOP].set_value(hk_state)
|
||||||
self._flag[PLAY_STOP] = False
|
self._flag[FEATURE_PLAY_STOP] = False
|
||||||
|
|
||||||
if self.chars[TOGGLE_MUTE]:
|
if self.chars[FEATURE_TOGGLE_MUTE]:
|
||||||
current_state = new_state.attributes.get(ATTR_MEDIA_VOLUME_MUTED)
|
current_state = new_state.attributes.get(ATTR_MEDIA_VOLUME_MUTED)
|
||||||
if not self._flag[TOGGLE_MUTE]:
|
if not self._flag[FEATURE_TOGGLE_MUTE]:
|
||||||
_LOGGER.debug('%s: Set current state for "toggle_mute" to %s',
|
_LOGGER.debug('%s: Set current state for "toggle_mute" to %s',
|
||||||
self.entity_id, current_state)
|
self.entity_id, current_state)
|
||||||
self.chars[TOGGLE_MUTE].set_value(current_state)
|
self.chars[FEATURE_TOGGLE_MUTE].set_value(current_state)
|
||||||
self._flag[TOGGLE_MUTE] = False
|
self._flag[FEATURE_TOGGLE_MUTE] = False
|
||||||
|
@ -3,20 +3,37 @@ import logging
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.media_player import (
|
import homeassistant.components.media_player as media_player
|
||||||
SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_STOP, SUPPORT_TURN_OFF,
|
|
||||||
SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE)
|
|
||||||
from homeassistant.core import split_entity_id
|
from homeassistant.core import split_entity_id
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_CODE, ATTR_SUPPORTED_FEATURES, CONF_MODE, CONF_NAME, TEMP_CELSIUS)
|
ATTR_CODE, ATTR_SUPPORTED_FEATURES, CONF_NAME, TEMP_CELSIUS)
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
import homeassistant.util.temperature as temp_util
|
import homeassistant.util.temperature as temp_util
|
||||||
from .const import (
|
from .const import (
|
||||||
HOMEKIT_NOTIFY_ID, ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE)
|
CONF_FEATURE, CONF_FEATURE_LIST, HOMEKIT_NOTIFY_ID, FEATURE_ON_OFF,
|
||||||
|
FEATURE_PLAY_PAUSE, FEATURE_PLAY_STOP, FEATURE_TOGGLE_MUTE)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
MEDIA_PLAYER_MODES = (ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE)
|
|
||||||
|
BASIC_INFO_SCHEMA = vol.Schema({
|
||||||
|
vol.Optional(CONF_NAME): cv.string,
|
||||||
|
})
|
||||||
|
|
||||||
|
FEATURE_SCHEMA = BASIC_INFO_SCHEMA.extend({
|
||||||
|
vol.Optional(CONF_FEATURE_LIST, default=None): cv.ensure_list,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
CODE_SCHEMA = BASIC_INFO_SCHEMA.extend({
|
||||||
|
vol.Optional(ATTR_CODE, default=None): vol.Any(None, cv.string),
|
||||||
|
})
|
||||||
|
|
||||||
|
MEDIA_PLAYER_SCHEMA = vol.Schema({
|
||||||
|
vol.Required(CONF_FEATURE): vol.All(
|
||||||
|
cv.string, vol.In((FEATURE_ON_OFF, FEATURE_PLAY_PAUSE,
|
||||||
|
FEATURE_PLAY_STOP, FEATURE_TOGGLE_MUTE))),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def validate_entity_config(values):
|
def validate_entity_config(values):
|
||||||
@ -24,57 +41,59 @@ def validate_entity_config(values):
|
|||||||
entities = {}
|
entities = {}
|
||||||
for entity_id, config in values.items():
|
for entity_id, config in values.items():
|
||||||
entity = cv.entity_id(entity_id)
|
entity = cv.entity_id(entity_id)
|
||||||
params = {}
|
|
||||||
if not isinstance(config, dict):
|
|
||||||
raise vol.Invalid('The configuration for "{}" must be '
|
|
||||||
' a dictionary.'.format(entity))
|
|
||||||
|
|
||||||
for key in (CONF_NAME, ):
|
|
||||||
value = config.get(key, -1)
|
|
||||||
if value != -1:
|
|
||||||
params[key] = cv.string(value)
|
|
||||||
|
|
||||||
domain, _ = split_entity_id(entity)
|
domain, _ = split_entity_id(entity)
|
||||||
|
|
||||||
|
if not isinstance(config, dict):
|
||||||
|
raise vol.Invalid('The configuration for {} must be '
|
||||||
|
' a dictionary.'.format(entity))
|
||||||
|
|
||||||
if domain in ('alarm_control_panel', 'lock'):
|
if domain in ('alarm_control_panel', 'lock'):
|
||||||
code = config.get(ATTR_CODE)
|
config = CODE_SCHEMA(config)
|
||||||
params[ATTR_CODE] = cv.string(code) if code else None
|
|
||||||
|
|
||||||
if domain == 'media_player':
|
elif domain == media_player.DOMAIN:
|
||||||
mode = config.get(CONF_MODE)
|
config = FEATURE_SCHEMA(config)
|
||||||
params[CONF_MODE] = cv.ensure_list(mode)
|
feature_list = {}
|
||||||
for key in params[CONF_MODE]:
|
for feature in config[CONF_FEATURE_LIST]:
|
||||||
if key not in MEDIA_PLAYER_MODES:
|
params = MEDIA_PLAYER_SCHEMA(feature)
|
||||||
raise vol.Invalid(
|
key = params.pop(CONF_FEATURE)
|
||||||
'Invalid mode: "{}", valid modes are: "{}".'
|
if key in feature_list:
|
||||||
.format(key, MEDIA_PLAYER_MODES))
|
raise vol.Invalid('A feature can be added only once for {}'
|
||||||
|
.format(entity))
|
||||||
|
feature_list[key] = params
|
||||||
|
config[CONF_FEATURE_LIST] = feature_list
|
||||||
|
|
||||||
entities[entity] = params
|
else:
|
||||||
|
config = BASIC_INFO_SCHEMA(config)
|
||||||
|
|
||||||
|
entities[entity] = config
|
||||||
return entities
|
return entities
|
||||||
|
|
||||||
|
|
||||||
def validate_media_player_modes(state, config):
|
def validate_media_player_features(state, feature_list):
|
||||||
"""Validate modes for media playeres."""
|
"""Validate features for media players."""
|
||||||
features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
features = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
||||||
|
|
||||||
supported_modes = []
|
supported_modes = []
|
||||||
if features & (SUPPORT_TURN_ON | SUPPORT_TURN_OFF):
|
if features & (media_player.SUPPORT_TURN_ON |
|
||||||
supported_modes.append(ON_OFF)
|
media_player.SUPPORT_TURN_OFF):
|
||||||
if features & (SUPPORT_PLAY | SUPPORT_PAUSE):
|
supported_modes.append(FEATURE_ON_OFF)
|
||||||
supported_modes.append(PLAY_PAUSE)
|
if features & (media_player.SUPPORT_PLAY | media_player.SUPPORT_PAUSE):
|
||||||
if features & (SUPPORT_PLAY | SUPPORT_STOP):
|
supported_modes.append(FEATURE_PLAY_PAUSE)
|
||||||
supported_modes.append(PLAY_STOP)
|
if features & (media_player.SUPPORT_PLAY | media_player.SUPPORT_STOP):
|
||||||
if features & SUPPORT_VOLUME_MUTE:
|
supported_modes.append(FEATURE_PLAY_STOP)
|
||||||
supported_modes.append(TOGGLE_MUTE)
|
if features & media_player.SUPPORT_VOLUME_MUTE:
|
||||||
|
supported_modes.append(FEATURE_TOGGLE_MUTE)
|
||||||
|
|
||||||
if not config.get(CONF_MODE):
|
error_list = []
|
||||||
config[CONF_MODE] = supported_modes
|
for feature in feature_list:
|
||||||
return
|
if feature not in supported_modes:
|
||||||
|
error_list.append(feature)
|
||||||
|
|
||||||
for mode in config[CONF_MODE]:
|
if error_list:
|
||||||
if mode not in supported_modes:
|
_LOGGER.error("%s does not support features: %s",
|
||||||
raise vol.Invalid('"{}" does not support mode: "{}".'
|
state.entity_id, error_list)
|
||||||
.format(state.entity_id, mode))
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def show_setup_message(hass, pincode):
|
def show_setup_message(hass, pincode):
|
||||||
|
@ -2,20 +2,17 @@
|
|||||||
from unittest.mock import patch, Mock
|
from unittest.mock import patch, Mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import voluptuous as vol
|
|
||||||
|
|
||||||
from homeassistant.core import State
|
from homeassistant.core import State
|
||||||
from homeassistant.components.cover import SUPPORT_CLOSE, SUPPORT_OPEN
|
import homeassistant.components.cover as cover
|
||||||
from homeassistant.components.climate import (
|
import homeassistant.components.climate as climate
|
||||||
SUPPORT_TARGET_TEMPERATURE_HIGH, SUPPORT_TARGET_TEMPERATURE_LOW)
|
import homeassistant.components.media_player as media_player
|
||||||
from homeassistant.components.media_player import (
|
|
||||||
SUPPORT_TURN_OFF, SUPPORT_TURN_ON)
|
|
||||||
from homeassistant.components.homekit import get_accessory, TYPES
|
from homeassistant.components.homekit import get_accessory, TYPES
|
||||||
from homeassistant.components.homekit.const import ON_OFF
|
from homeassistant.components.homekit.const import (
|
||||||
|
CONF_FEATURE_LIST, FEATURE_ON_OFF)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_CODE, ATTR_DEVICE_CLASS, ATTR_SUPPORTED_FEATURES,
|
ATTR_CODE, ATTR_DEVICE_CLASS, ATTR_SUPPORTED_FEATURES,
|
||||||
ATTR_UNIT_OF_MEASUREMENT, CONF_MODE, CONF_NAME, TEMP_CELSIUS,
|
ATTR_UNIT_OF_MEASUREMENT, CONF_NAME, TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||||
TEMP_FAHRENHEIT)
|
|
||||||
|
|
||||||
|
|
||||||
def test_not_supported(caplog):
|
def test_not_supported(caplog):
|
||||||
@ -32,9 +29,9 @@ def test_not_supported(caplog):
|
|||||||
def test_not_supported_media_player():
|
def test_not_supported_media_player():
|
||||||
"""Test if mode isn't supported and if no supported modes."""
|
"""Test if mode isn't supported and if no supported modes."""
|
||||||
# selected mode for entity not supported
|
# selected mode for entity not supported
|
||||||
with pytest.raises(vol.Invalid):
|
config = {CONF_FEATURE_LIST: {FEATURE_ON_OFF: None}}
|
||||||
entity_state = State('media_player.demo', 'on')
|
entity_state = State('media_player.demo', 'on')
|
||||||
get_accessory(None, entity_state, 2, {CONF_MODE: [ON_OFF]})
|
get_accessory(None, entity_state, 2, config) is None
|
||||||
|
|
||||||
# no supported modes for entity
|
# no supported modes for entity
|
||||||
entity_state = State('media_player.demo', 'on')
|
entity_state = State('media_player.demo', 'on')
|
||||||
@ -58,14 +55,15 @@ def test_customize_options(config, name):
|
|||||||
('Light', 'light.test', 'on', {}, {}),
|
('Light', 'light.test', 'on', {}, {}),
|
||||||
('Lock', 'lock.test', 'locked', {}, {ATTR_CODE: '1234'}),
|
('Lock', 'lock.test', 'locked', {}, {ATTR_CODE: '1234'}),
|
||||||
('MediaPlayer', 'media_player.test', 'on',
|
('MediaPlayer', 'media_player.test', 'on',
|
||||||
{ATTR_SUPPORTED_FEATURES: SUPPORT_TURN_ON | SUPPORT_TURN_OFF},
|
{ATTR_SUPPORTED_FEATURES: media_player.SUPPORT_TURN_ON |
|
||||||
{CONF_MODE: [ON_OFF]}),
|
media_player.SUPPORT_TURN_OFF}, {CONF_FEATURE_LIST:
|
||||||
|
{FEATURE_ON_OFF: None}}),
|
||||||
('SecuritySystem', 'alarm_control_panel.test', 'armed', {},
|
('SecuritySystem', 'alarm_control_panel.test', 'armed', {},
|
||||||
{ATTR_CODE: '1234'}),
|
{ATTR_CODE: '1234'}),
|
||||||
('Thermostat', 'climate.test', 'auto', {}, {}),
|
('Thermostat', 'climate.test', 'auto', {}, {}),
|
||||||
('Thermostat', 'climate.test', 'auto',
|
('Thermostat', 'climate.test', 'auto',
|
||||||
{ATTR_SUPPORTED_FEATURES: SUPPORT_TARGET_TEMPERATURE_LOW |
|
{ATTR_SUPPORTED_FEATURES: climate.SUPPORT_TARGET_TEMPERATURE_LOW |
|
||||||
SUPPORT_TARGET_TEMPERATURE_HIGH}, {}),
|
climate.SUPPORT_TARGET_TEMPERATURE_HIGH}, {}),
|
||||||
])
|
])
|
||||||
def test_types(type_name, entity_id, state, attrs, config):
|
def test_types(type_name, entity_id, state, attrs, config):
|
||||||
"""Test if types are associated correctly."""
|
"""Test if types are associated correctly."""
|
||||||
@ -82,7 +80,7 @@ def test_types(type_name, entity_id, state, attrs, config):
|
|||||||
@pytest.mark.parametrize('type_name, entity_id, state, attrs', [
|
@pytest.mark.parametrize('type_name, entity_id, state, attrs', [
|
||||||
('GarageDoorOpener', 'cover.garage_door', 'open',
|
('GarageDoorOpener', 'cover.garage_door', 'open',
|
||||||
{ATTR_DEVICE_CLASS: 'garage',
|
{ATTR_DEVICE_CLASS: 'garage',
|
||||||
ATTR_SUPPORTED_FEATURES: SUPPORT_OPEN | SUPPORT_CLOSE}),
|
ATTR_SUPPORTED_FEATURES: cover.SUPPORT_OPEN | cover.SUPPORT_CLOSE}),
|
||||||
('WindowCovering', 'cover.set_position', 'open',
|
('WindowCovering', 'cover.set_position', 'open',
|
||||||
{ATTR_SUPPORTED_FEATURES: 4}),
|
{ATTR_SUPPORTED_FEATURES: 4}),
|
||||||
('WindowCoveringBasic', 'cover.open_window', 'open',
|
('WindowCoveringBasic', 'cover.open_window', 'open',
|
||||||
|
@ -4,9 +4,10 @@ from homeassistant.components.media_player import (
|
|||||||
ATTR_MEDIA_VOLUME_MUTED, DOMAIN)
|
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 (
|
from homeassistant.components.homekit.const import (
|
||||||
ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE)
|
CONF_FEATURE_LIST, FEATURE_ON_OFF, FEATURE_PLAY_PAUSE, FEATURE_PLAY_STOP,
|
||||||
|
FEATURE_TOGGLE_MUTE)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, CONF_MODE, SERVICE_MEDIA_PAUSE,
|
ATTR_ENTITY_ID, ATTR_SUPPORTED_FEATURES, SERVICE_MEDIA_PAUSE,
|
||||||
SERVICE_MEDIA_PLAY, SERVICE_MEDIA_STOP, SERVICE_TURN_OFF, SERVICE_TURN_ON,
|
SERVICE_MEDIA_PLAY, SERVICE_MEDIA_STOP, SERVICE_TURN_OFF, SERVICE_TURN_ON,
|
||||||
SERVICE_VOLUME_MUTE, STATE_IDLE, STATE_OFF, STATE_ON, STATE_PAUSED,
|
SERVICE_VOLUME_MUTE, STATE_IDLE, STATE_OFF, STATE_ON, STATE_PAUSED,
|
||||||
STATE_PLAYING)
|
STATE_PLAYING)
|
||||||
@ -16,7 +17,9 @@ from tests.common import async_mock_service
|
|||||||
|
|
||||||
async def test_media_player_set_state(hass):
|
async def test_media_player_set_state(hass):
|
||||||
"""Test if accessory and HA are updated accordingly."""
|
"""Test if accessory and HA are updated accordingly."""
|
||||||
config = {CONF_MODE: [ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE]}
|
config = {CONF_FEATURE_LIST: {
|
||||||
|
FEATURE_ON_OFF: None, FEATURE_PLAY_PAUSE: None,
|
||||||
|
FEATURE_PLAY_STOP: None, FEATURE_TOGGLE_MUTE: None}}
|
||||||
entity_id = 'media_player.test'
|
entity_id = 'media_player.test'
|
||||||
|
|
||||||
hass.states.async_set(entity_id, None, {ATTR_SUPPORTED_FEATURES: 20873,
|
hass.states.async_set(entity_id, None, {ATTR_SUPPORTED_FEATURES: 20873,
|
||||||
@ -28,32 +31,32 @@ async def test_media_player_set_state(hass):
|
|||||||
assert acc.aid == 2
|
assert acc.aid == 2
|
||||||
assert acc.category == 8 # Switch
|
assert acc.category == 8 # Switch
|
||||||
|
|
||||||
assert acc.chars[ON_OFF].value == 0
|
assert acc.chars[FEATURE_ON_OFF].value == 0
|
||||||
assert acc.chars[PLAY_PAUSE].value == 0
|
assert acc.chars[FEATURE_PLAY_PAUSE].value == 0
|
||||||
assert acc.chars[PLAY_STOP].value == 0
|
assert acc.chars[FEATURE_PLAY_STOP].value == 0
|
||||||
assert acc.chars[TOGGLE_MUTE].value == 0
|
assert acc.chars[FEATURE_TOGGLE_MUTE].value == 0
|
||||||
|
|
||||||
hass.states.async_set(entity_id, STATE_ON, {ATTR_MEDIA_VOLUME_MUTED: True})
|
hass.states.async_set(entity_id, STATE_ON, {ATTR_MEDIA_VOLUME_MUTED: True})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.chars[ON_OFF].value == 1
|
assert acc.chars[FEATURE_ON_OFF].value == 1
|
||||||
assert acc.chars[TOGGLE_MUTE].value == 1
|
assert acc.chars[FEATURE_TOGGLE_MUTE].value == 1
|
||||||
|
|
||||||
hass.states.async_set(entity_id, STATE_OFF)
|
hass.states.async_set(entity_id, STATE_OFF)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.chars[ON_OFF].value == 0
|
assert acc.chars[FEATURE_ON_OFF].value == 0
|
||||||
|
|
||||||
hass.states.async_set(entity_id, STATE_PLAYING)
|
hass.states.async_set(entity_id, STATE_PLAYING)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.chars[PLAY_PAUSE].value == 1
|
assert acc.chars[FEATURE_PLAY_PAUSE].value == 1
|
||||||
assert acc.chars[PLAY_STOP].value == 1
|
assert acc.chars[FEATURE_PLAY_STOP].value == 1
|
||||||
|
|
||||||
hass.states.async_set(entity_id, STATE_PAUSED)
|
hass.states.async_set(entity_id, STATE_PAUSED)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.chars[PLAY_PAUSE].value == 0
|
assert acc.chars[FEATURE_PLAY_PAUSE].value == 0
|
||||||
|
|
||||||
hass.states.async_set(entity_id, STATE_IDLE)
|
hass.states.async_set(entity_id, STATE_IDLE)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert acc.chars[PLAY_STOP].value == 0
|
assert acc.chars[FEATURE_PLAY_STOP].value == 0
|
||||||
|
|
||||||
# Set from HomeKit
|
# Set from HomeKit
|
||||||
call_turn_on = async_mock_service(hass, DOMAIN, SERVICE_TURN_ON)
|
call_turn_on = async_mock_service(hass, DOMAIN, SERVICE_TURN_ON)
|
||||||
@ -63,43 +66,51 @@ async def test_media_player_set_state(hass):
|
|||||||
call_media_stop = async_mock_service(hass, DOMAIN, SERVICE_MEDIA_STOP)
|
call_media_stop = async_mock_service(hass, DOMAIN, SERVICE_MEDIA_STOP)
|
||||||
call_toggle_mute = async_mock_service(hass, DOMAIN, SERVICE_VOLUME_MUTE)
|
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_add_job(acc.chars[FEATURE_ON_OFF]
|
||||||
|
.client_update_value, True)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert call_turn_on
|
assert call_turn_on
|
||||||
assert call_turn_on[0].data[ATTR_ENTITY_ID] == entity_id
|
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_add_job(acc.chars[FEATURE_ON_OFF]
|
||||||
|
.client_update_value, False)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert call_turn_off
|
assert call_turn_off
|
||||||
assert call_turn_off[0].data[ATTR_ENTITY_ID] == entity_id
|
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_add_job(acc.chars[FEATURE_PLAY_PAUSE]
|
||||||
|
.client_update_value, True)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert call_media_play
|
assert call_media_play
|
||||||
assert call_media_play[0].data[ATTR_ENTITY_ID] == entity_id
|
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_add_job(acc.chars[FEATURE_PLAY_PAUSE]
|
||||||
|
.client_update_value, False)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert call_media_pause
|
assert call_media_pause
|
||||||
assert call_media_pause[0].data[ATTR_ENTITY_ID] == entity_id
|
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_add_job(acc.chars[FEATURE_PLAY_STOP]
|
||||||
|
.client_update_value, True)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert call_media_play
|
assert call_media_play
|
||||||
assert call_media_play[1].data[ATTR_ENTITY_ID] == entity_id
|
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_add_job(acc.chars[FEATURE_PLAY_STOP]
|
||||||
|
.client_update_value, False)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert call_media_stop
|
assert call_media_stop
|
||||||
assert call_media_stop[0].data[ATTR_ENTITY_ID] == entity_id
|
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_add_job(acc.chars[FEATURE_TOGGLE_MUTE]
|
||||||
|
.client_update_value, True)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert call_toggle_mute
|
assert call_toggle_mute
|
||||||
assert call_toggle_mute[0].data[ATTR_ENTITY_ID] == entity_id
|
assert call_toggle_mute[0].data[ATTR_ENTITY_ID] == entity_id
|
||||||
assert call_toggle_mute[0].data[ATTR_MEDIA_VOLUME_MUTED] is True
|
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_add_job(acc.chars[FEATURE_TOGGLE_MUTE]
|
||||||
|
.client_update_value, False)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert call_toggle_mute
|
assert call_toggle_mute
|
||||||
assert call_toggle_mute[1].data[ATTR_ENTITY_ID] == entity_id
|
assert call_toggle_mute[1].data[ATTR_ENTITY_ID] == entity_id
|
||||||
|
@ -4,17 +4,18 @@ import voluptuous as vol
|
|||||||
|
|
||||||
from homeassistant.core import State
|
from homeassistant.core import State
|
||||||
from homeassistant.components.homekit.const import (
|
from homeassistant.components.homekit.const import (
|
||||||
HOMEKIT_NOTIFY_ID, ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE)
|
CONF_FEATURE, CONF_FEATURE_LIST, HOMEKIT_NOTIFY_ID, FEATURE_ON_OFF,
|
||||||
|
FEATURE_PLAY_PAUSE)
|
||||||
from homeassistant.components.homekit.util import (
|
from homeassistant.components.homekit.util import (
|
||||||
convert_to_float, density_to_air_quality, dismiss_setup_message,
|
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)
|
validate_media_player_features)
|
||||||
from homeassistant.components.homekit.util import validate_entity_config \
|
from homeassistant.components.homekit.util import validate_entity_config \
|
||||||
as vec
|
as vec
|
||||||
from homeassistant.components.persistent_notification import (
|
from homeassistant.components.persistent_notification import (
|
||||||
ATTR_MESSAGE, ATTR_NOTIFICATION_ID, DOMAIN)
|
ATTR_MESSAGE, ATTR_NOTIFICATION_ID, DOMAIN)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_CODE, ATTR_SUPPORTED_FEATURES, CONF_MODE, CONF_NAME, STATE_UNKNOWN,
|
ATTR_CODE, ATTR_SUPPORTED_FEATURES, CONF_NAME, STATE_UNKNOWN,
|
||||||
TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||||
|
|
||||||
from tests.common import async_mock_service
|
from tests.common import async_mock_service
|
||||||
@ -25,7 +26,11 @@ def test_validate_entity_config():
|
|||||||
configs = [{'invalid_entity_id': {}}, {'demo.test': 1},
|
configs = [{'invalid_entity_id': {}}, {'demo.test': 1},
|
||||||
{'demo.test': 'test'}, {'demo.test': [1, 2]},
|
{'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: 'invalid_mode'}}]
|
{'media_player.test': {CONF_FEATURE_LIST: [
|
||||||
|
{CONF_FEATURE: 'invalid_feature'}]}},
|
||||||
|
{'media_player.test': {CONF_FEATURE_LIST: [
|
||||||
|
{CONF_FEATURE: FEATURE_ON_OFF},
|
||||||
|
{CONF_FEATURE: FEATURE_ON_OFF}]}}, ]
|
||||||
|
|
||||||
for conf in configs:
|
for conf in configs:
|
||||||
with pytest.raises(vol.Invalid):
|
with pytest.raises(vol.Invalid):
|
||||||
@ -45,23 +50,26 @@ def test_validate_entity_config():
|
|||||||
{'lock.demo': {ATTR_CODE: '1234'}}
|
{'lock.demo': {ATTR_CODE: '1234'}}
|
||||||
|
|
||||||
assert vec({'media_player.demo': {}}) == \
|
assert vec({'media_player.demo': {}}) == \
|
||||||
{'media_player.demo': {CONF_MODE: []}}
|
{'media_player.demo': {CONF_FEATURE_LIST: {}}}
|
||||||
assert vec({'media_player.demo': {CONF_MODE: [ON_OFF]}}) == \
|
config = {CONF_FEATURE_LIST: [{CONF_FEATURE: FEATURE_ON_OFF},
|
||||||
{'media_player.demo': {CONF_MODE: [ON_OFF]}}
|
{CONF_FEATURE: FEATURE_PLAY_PAUSE}]}
|
||||||
|
assert vec({'media_player.demo': config}) == \
|
||||||
|
{'media_player.demo': {CONF_FEATURE_LIST:
|
||||||
|
{FEATURE_ON_OFF: {}, FEATURE_PLAY_PAUSE: {}}}}
|
||||||
|
|
||||||
|
|
||||||
def test_validate_media_player_modes():
|
def test_validate_media_player_features():
|
||||||
"""Test validate modes for media players."""
|
"""Test validate modes for media players."""
|
||||||
config = {}
|
config = {}
|
||||||
attrs = {ATTR_SUPPORTED_FEATURES: 20873}
|
attrs = {ATTR_SUPPORTED_FEATURES: 20873}
|
||||||
entity_state = State('media_player.demo', 'on', attrs)
|
entity_state = State('media_player.demo', 'on', attrs)
|
||||||
validate_media_player_modes(entity_state, config)
|
assert validate_media_player_features(entity_state, config) is True
|
||||||
assert config == {CONF_MODE: [ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE]}
|
|
||||||
|
config = {FEATURE_ON_OFF: None}
|
||||||
|
assert validate_media_player_features(entity_state, config) is True
|
||||||
|
|
||||||
entity_state = State('media_player.demo', 'on')
|
entity_state = State('media_player.demo', 'on')
|
||||||
config = {CONF_MODE: [ON_OFF]}
|
assert validate_media_player_features(entity_state, config) is False
|
||||||
with pytest.raises(vol.Invalid):
|
|
||||||
validate_media_player_modes(entity_state, config)
|
|
||||||
|
|
||||||
|
|
||||||
def test_convert_to_float():
|
def test_convert_to_float():
|
||||||
|
Loading…
x
Reference in New Issue
Block a user