Add HomeKit support for media players (#14446)
This commit is contained in:
parent
d53a8c0823
commit
a9f19a16ee
7 changed files with 354 additions and 11 deletions
|
@ -13,7 +13,7 @@ from homeassistant.components.cover import (
|
|||
SUPPORT_CLOSE, SUPPORT_OPEN, SUPPORT_SET_POSITION)
|
||||
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)
|
||||
|
@ -25,7 +25,8 @@ 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)
|
||||
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__)
|
||||
|
@ -125,6 +126,11 @@ def get_accessory(hass, state, aid, config):
|
|||
elif state.domain == 'lock':
|
||||
a_type = 'Lock'
|
||||
|
||||
elif state.domain == 'media_player':
|
||||
validate_media_player_modes(state, config)
|
||||
if config.get(CONF_MODE):
|
||||
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 +214,8 @@ class HomeKit():
|
|||
# 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)
|
||||
|
|
|
@ -23,6 +23,12 @@ BRIDGE_NAME = 'Home Assistant Bridge'
|
|||
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'
|
||||
|
|
142
homeassistant/components/homekit/type_media_players.py
Normal file
142
homeassistant/components/homekit/type_media_players.py
Normal file
|
@ -0,0 +1,142 @@
|
|||
"""Class to hold all media player accessories."""
|
||||
import logging
|
||||
|
||||
from pyhap.const import CATEGORY_SWITCH
|
||||
|
||||
from homeassistant.const import (
|
||||
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 (
|
||||
CHAR_NAME, CHAR_ON, ON_OFF, PLAY_PAUSE, PLAY_STOP, SERV_SWITCH,
|
||||
TOGGLE_MUTE)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
MODE_FRIENDLY_NAME = {ON_OFF: 'Power',
|
||||
PLAY_PAUSE: 'Play/Pause',
|
||||
PLAY_STOP: 'Play/Stop',
|
||||
TOGGLE_MUTE: '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._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[CONF_MODE]
|
||||
|
||||
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.chars[ON_OFF] = serv_on_off.configure_char(
|
||||
CHAR_ON, value=False, setter_callback=self.set_on_off)
|
||||
|
||||
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.chars[PLAY_PAUSE] = serv_play_pause.configure_char(
|
||||
CHAR_ON, value=False, setter_callback=self.set_play_pause)
|
||||
|
||||
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.chars[PLAY_STOP] = serv_play_stop.configure_char(
|
||||
CHAR_ON, value=False, setter_callback=self.set_play_stop)
|
||||
|
||||
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.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.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._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)
|
||||
|
||||
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._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)
|
||||
|
||||
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._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)
|
||||
|
||||
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._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)
|
||||
|
||||
def update_state(self, new_state):
|
||||
"""Update switch state after state changed."""
|
||||
current_state = new_state.state
|
||||
|
||||
if self.chars[ON_OFF]:
|
||||
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)
|
||||
self.chars[ON_OFF].set_value(hk_state)
|
||||
self._flag[ON_OFF] = False
|
||||
|
||||
if self.chars[PLAY_PAUSE]:
|
||||
hk_state = current_state == STATE_PLAYING
|
||||
if not self._flag[PLAY_PAUSE]:
|
||||
_LOGGER.debug('%s: Set current state for "play_pause" to %s',
|
||||
self.entity_id, hk_state)
|
||||
self.chars[PLAY_PAUSE].set_value(hk_state)
|
||||
self._flag[PLAY_PAUSE] = False
|
||||
|
||||
if self.chars[PLAY_STOP]:
|
||||
hk_state = current_state == STATE_PLAYING
|
||||
if not self._flag[PLAY_STOP]:
|
||||
_LOGGER.debug('%s: Set current state for "play_stop" to %s',
|
||||
self.entity_id, hk_state)
|
||||
self.chars[PLAY_STOP].set_value(hk_state)
|
||||
self._flag[PLAY_STOP] = False
|
||||
|
||||
if self.chars[TOGGLE_MUTE]:
|
||||
current_state = new_state.attributes.get(ATTR_MEDIA_VOLUME_MUTED)
|
||||
if not self._flag[TOGGLE_MUTE]:
|
||||
_LOGGER.debug('%s: Set current state for "toggle_mute" to %s',
|
||||
self.entity_id, current_state)
|
||||
self.chars[TOGGLE_MUTE].set_value(current_state)
|
||||
self._flag[TOGGLE_MUTE] = False
|
|
@ -3,15 +3,21 @@ import logging
|
|||
|
||||
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)
|
||||
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."""
|
||||
|
@ -34,10 +40,43 @@ def validate_entity_config(values):
|
|||
code = config.get(ATTR_CODE)
|
||||
params[ATTR_CODE] = cv.string(code) if code else None
|
||||
|
||||
if domain == 'media_player':
|
||||
mode = config.get(CONF_MODE)
|
||||
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
|
||||
|
||||
|
||||
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)
|
||||
|
||||
if not config.get(CONF_MODE):
|
||||
config[CONF_MODE] = supported_modes
|
||||
return
|
||||
|
||||
for mode in config[CONF_MODE]:
|
||||
if mode not in supported_modes:
|
||||
raise vol.Invalid('"{}" does not support mode: "{}".'
|
||||
.format(state.entity_id, mode))
|
||||
|
||||
|
||||
def show_setup_message(hass, pincode):
|
||||
"""Display persistent notification with setup information."""
|
||||
pin = pincode.decode()
|
||||
|
|
|
@ -2,15 +2,20 @@
|
|||
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_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_NAME, TEMP_CELSIUS, TEMP_FAHRENHEIT)
|
||||
ATTR_UNIT_OF_MEASUREMENT, CONF_MODE, CONF_NAME, TEMP_CELSIUS,
|
||||
TEMP_FAHRENHEIT)
|
||||
|
||||
|
||||
def test_not_supported(caplog):
|
||||
|
@ -24,6 +29,18 @@ def test_not_supported(caplog):
|
|||
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):
|
||||
entity_state = State('media_player.demo', 'on')
|
||||
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'),
|
||||
])
|
||||
|
@ -40,6 +57,9 @@ def test_customize_options(config, name):
|
|||
('Fan', 'fan.test', 'on', {}, {}),
|
||||
('Light', 'light.test', 'on', {}, {}),
|
||||
('Lock', 'lock.test', 'locked', {}, {ATTR_CODE: '1234'}),
|
||||
('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', {}, {}),
|
||||
|
|
106
tests/components/homekit/test_type_media_players.py
Normal file
106
tests/components/homekit/test_type_media_players.py
Normal file
|
@ -0,0 +1,106 @@
|
|||
"""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.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_IDLE, STATE_OFF, STATE_ON, STATE_PAUSED,
|
||||
STATE_PLAYING)
|
||||
|
||||
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
|
|
@ -2,16 +2,20 @@
|
|||
import pytest
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.homekit.const import HOMEKIT_NOTIFY_ID
|
||||
from homeassistant.core import State
|
||||
from homeassistant.components.homekit.const import (
|
||||
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)
|
||||
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 +24,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: 'invalid_mode'}}]
|
||||
|
||||
for conf in configs:
|
||||
with pytest.raises(vol.Invalid):
|
||||
|
@ -39,6 +44,25 @@ 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': {CONF_MODE: []}}
|
||||
assert vec({'media_player.demo': {CONF_MODE: [ON_OFF]}}) == \
|
||||
{'media_player.demo': {CONF_MODE: [ON_OFF]}}
|
||||
|
||||
|
||||
def test_validate_media_player_modes():
|
||||
"""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, config)
|
||||
assert config == {CONF_MODE: [ON_OFF, PLAY_PAUSE, PLAY_STOP, TOGGLE_MUTE]}
|
||||
|
||||
entity_state = State('media_player.demo', 'on')
|
||||
config = {CONF_MODE: [ON_OFF]}
|
||||
with pytest.raises(vol.Invalid):
|
||||
validate_media_player_modes(entity_state, config)
|
||||
|
||||
|
||||
def test_convert_to_float():
|
||||
"""Test convert_to_float method."""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue