From 39932d132ddf4b6c02f2fc68961bf0e76245f0f1 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Thu, 25 Apr 2019 22:12:11 +0200 Subject: [PATCH] Add device classes for media player and map to google types (#23236) * Add device classes for media player and map to google types * Switch default class for media_player to media --- homeassistant/components/demo/media_player.py | 8 +++- .../components/google_assistant/const.py | 7 +++- .../components/media_player/__init__.py | 10 +++++ tests/components/google_assistant/__init__.py | 8 ++-- .../google_assistant/test_smart_home.py | 41 +++++++++++++++++++ 5 files changed, 68 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/demo/media_player.py b/homeassistant/components/demo/media_player.py index cb3f3b5b46a..5a97b43af86 100644 --- a/homeassistant/components/demo/media_player.py +++ b/homeassistant/components/demo/media_player.py @@ -51,7 +51,7 @@ class AbstractDemoPlayer(MediaPlayerDevice): # We only implement the methods that we support - def __init__(self, name): + def __init__(self, name, device_class=None): """Initialize the demo device.""" self._name = name self._player_state = STATE_PLAYING @@ -60,6 +60,7 @@ class AbstractDemoPlayer(MediaPlayerDevice): self._shuffle = False self._sound_mode_list = SOUND_MODE_LIST self._sound_mode = DEFAULT_SOUND_MODE + self._device_class = device_class @property def should_poll(self): @@ -101,6 +102,11 @@ class AbstractDemoPlayer(MediaPlayerDevice): """Return a list of available sound modes.""" return self._sound_mode_list + @property + def device_class(self): + """Return the device class of the media player.""" + return self._device_class + def turn_on(self): """Turn the media player on.""" self._player_state = STATE_PLAYING diff --git a/homeassistant/components/google_assistant/const.py b/homeassistant/components/google_assistant/const.py index b6f57546cec..0f15d10f181 100644 --- a/homeassistant/components/google_assistant/const.py +++ b/homeassistant/components/google_assistant/const.py @@ -51,6 +51,9 @@ TYPE_GARAGE = PREFIX_TYPES + 'GARAGE' TYPE_OUTLET = PREFIX_TYPES + 'OUTLET' TYPE_SENSOR = PREFIX_TYPES + 'SENSOR' TYPE_DOOR = PREFIX_TYPES + 'DOOR' +TYPE_TV = PREFIX_TYPES + 'TV' +TYPE_SPEAKER = PREFIX_TYPES + 'SPEAKER' +TYPE_MEDIA = PREFIX_TYPES + 'MEDIA' SERVICE_REQUEST_SYNC = 'request_sync' HOMEGRAPH_URL = 'https://homegraph.googleapis.com/' @@ -86,7 +89,7 @@ DOMAIN_TO_GOOGLE_TYPES = { input_boolean.DOMAIN: TYPE_SWITCH, light.DOMAIN: TYPE_LIGHT, lock.DOMAIN: TYPE_LOCK, - media_player.DOMAIN: TYPE_SWITCH, + media_player.DOMAIN: TYPE_MEDIA, scene.DOMAIN: TYPE_SCENE, script.DOMAIN: TYPE_SCENE, switch.DOMAIN: TYPE_SWITCH, @@ -104,6 +107,8 @@ DEVICE_CLASS_TO_GOOGLE_TYPES = { (binary_sensor.DOMAIN, binary_sensor.DEVICE_CLASS_LOCK): TYPE_SENSOR, (binary_sensor.DOMAIN, binary_sensor.DEVICE_CLASS_OPENING): TYPE_SENSOR, (binary_sensor.DOMAIN, binary_sensor.DEVICE_CLASS_WINDOW): TYPE_SENSOR, + (media_player.DOMAIN, media_player.DEVICE_CLASS_TV): TYPE_TV, + (media_player.DOMAIN, media_player.DEVICE_CLASS_SPEAKER): TYPE_SPEAKER, } CHALLENGE_ACK_NEEDED = 'ackNeeded' diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 478f59d2817..b23d95ab625 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -66,6 +66,16 @@ ENTITY_IMAGE_CACHE = { SCAN_INTERVAL = timedelta(seconds=10) +DEVICE_CLASS_TV = 'tv' +DEVICE_CLASS_SPEAKER = 'speaker' + +DEVICE_CLASSES = [ + DEVICE_CLASS_TV, + DEVICE_CLASS_SPEAKER, +] + +DEVICE_CLASSES_SCHEMA = vol.All(vol.Lower, vol.In(DEVICE_CLASSES)) + # Service call validation schemas MEDIA_PLAYER_SCHEMA = vol.Schema({ ATTR_ENTITY_ID: cv.comp_entity_ids, diff --git a/tests/components/google_assistant/__init__.py b/tests/components/google_assistant/__init__.py index f3732c12213..76ccc79a378 100644 --- a/tests/components/google_assistant/__init__.py +++ b/tests/components/google_assistant/__init__.py @@ -147,7 +147,7 @@ DEMO_DEVICES = [{ 'action.devices.traits.Modes' ], 'type': - 'action.devices.types.SWITCH', + 'action.devices.types.MEDIA', 'willReportState': False }, { @@ -162,7 +162,7 @@ DEMO_DEVICES = [{ 'action.devices.traits.Modes' ], 'type': - 'action.devices.types.SWITCH', + 'action.devices.types.MEDIA', 'willReportState': False }, { @@ -171,7 +171,7 @@ DEMO_DEVICES = [{ 'name': 'Lounge room' }, 'traits': ['action.devices.traits.OnOff', 'action.devices.traits.Modes'], - 'type': 'action.devices.types.SWITCH', + 'type': 'action.devices.types.MEDIA', 'willReportState': False }, { 'id': @@ -182,7 +182,7 @@ DEMO_DEVICES = [{ 'traits': ['action.devices.traits.OnOff', 'action.devices.traits.Volume'], 'type': - 'action.devices.types.SWITCH', + 'action.devices.types.MEDIA', 'willReportState': False }, { diff --git a/tests/components/google_assistant/test_smart_home.py b/tests/components/google_assistant/test_smart_home.py index ce750b74e23..ea5291f28f7 100644 --- a/tests/components/google_assistant/test_smart_home.py +++ b/tests/components/google_assistant/test_smart_home.py @@ -16,6 +16,7 @@ from homeassistant.components.google_assistant import ( from homeassistant.components.demo.binary_sensor import DemoBinarySensor from homeassistant.components.demo.cover import DemoCover from homeassistant.components.demo.light import DemoLight +from homeassistant.components.demo.media_player import AbstractDemoPlayer from homeassistant.components.demo.switch import DemoSwitch from homeassistant.helpers import device_registry @@ -684,6 +685,46 @@ async def test_device_class_cover(hass, device_class, google_type): } +@pytest.mark.parametrize("device_class,google_type", [ + ('non_existing_class', 'action.devices.types.MEDIA'), + ('speaker', 'action.devices.types.SPEAKER'), + ('tv', 'action.devices.types.TV'), +]) +async def test_device_media_player(hass, device_class, google_type): + """Test that a binary entity syncs to the correct device type.""" + sensor = AbstractDemoPlayer( + 'Demo', + device_class=device_class + ) + sensor.hass = hass + sensor.entity_id = 'media_player.demo' + await sensor.async_update_ha_state() + + result = await sh.async_handle_message( + hass, BASIC_CONFIG, 'test-agent', + { + "requestId": REQ_ID, + "inputs": [{ + "intent": "action.devices.SYNC" + }] + }) + + assert result == { + 'requestId': REQ_ID, + 'payload': { + 'agentUserId': 'test-agent', + 'devices': [{ + 'attributes': {}, + 'id': sensor.entity_id, + 'name': {'name': sensor.name}, + 'traits': ['action.devices.traits.OnOff'], + 'type': google_type, + 'willReportState': False + }] + } + } + + async def test_query_disconnect(hass): """Test a disconnect message.""" result = await sh.async_handle_message(