Add select entity to Logitech Harmony (#53943)
Co-authored-by: J. Nick Koston <nick@koston.org>pull/55391/head
parent
778fa2e3fe
commit
979797136a
|
@ -202,7 +202,7 @@ homeassistant/components/group/* @home-assistant/core
|
|||
homeassistant/components/growatt_server/* @indykoning @muppet3000 @JasperPlant
|
||||
homeassistant/components/guardian/* @bachya
|
||||
homeassistant/components/habitica/* @ASMfreaK @leikoilja
|
||||
homeassistant/components/harmony/* @ehendrix23 @bramkragten @bdraco @mkeesey
|
||||
homeassistant/components/harmony/* @ehendrix23 @bramkragten @bdraco @mkeesey @Aohzan
|
||||
homeassistant/components/hassio/* @home-assistant/supervisor
|
||||
homeassistant/components/heatmiser/* @andylockran
|
||||
homeassistant/components/heos/* @andrewsayre
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
DOMAIN = "harmony"
|
||||
SERVICE_SYNC = "sync"
|
||||
SERVICE_CHANGE_CHANNEL = "change_channel"
|
||||
PLATFORMS = ["remote", "switch"]
|
||||
PLATFORMS = ["remote", "switch", "select"]
|
||||
UNIQUE_ID = "unique_id"
|
||||
ACTIVITY_POWER_OFF = "PowerOff"
|
||||
HARMONY_OPTIONS_UPDATE = "harmony_options_update"
|
||||
|
|
|
@ -1,20 +1,31 @@
|
|||
"""Mixin class for handling connection state changes."""
|
||||
"""Base class Harmony entities."""
|
||||
import logging
|
||||
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.event import async_call_later
|
||||
|
||||
from .data import HarmonyData
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
TIME_MARK_DISCONNECTED = 10
|
||||
|
||||
|
||||
class ConnectionStateMixin:
|
||||
"""Base implementation for connection state handling."""
|
||||
class HarmonyEntity(Entity):
|
||||
"""Base entity for Harmony with connection state handling."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize this mixin instance."""
|
||||
def __init__(self, data: HarmonyData) -> None:
|
||||
"""Initialize the Harmony base entity."""
|
||||
super().__init__()
|
||||
self._unsub_mark_disconnected = None
|
||||
self._name = data.name
|
||||
self._data = data
|
||||
self._attr_should_poll = False
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return True if we're connected to the Hub, otherwise False."""
|
||||
return self._data.available
|
||||
|
||||
async def async_got_connected(self, _=None):
|
||||
"""Notification that we're connected to the HUB."""
|
|
@ -3,7 +3,13 @@
|
|||
"name": "Logitech Harmony Hub",
|
||||
"documentation": "https://www.home-assistant.io/integrations/harmony",
|
||||
"requirements": ["aioharmony==0.2.7"],
|
||||
"codeowners": ["@ehendrix23", "@bramkragten", "@bdraco", "@mkeesey"],
|
||||
"codeowners": [
|
||||
"@ehendrix23",
|
||||
"@bramkragten",
|
||||
"@bdraco",
|
||||
"@mkeesey",
|
||||
"@Aohzan"
|
||||
],
|
||||
"ssdp": [
|
||||
{
|
||||
"manufacturer": "Logitech",
|
||||
|
|
|
@ -21,7 +21,6 @@ import homeassistant.helpers.config_validation as cv
|
|||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
|
||||
from .connection_state import ConnectionStateMixin
|
||||
from .const import (
|
||||
ACTIVITY_POWER_OFF,
|
||||
ATTR_ACTIVITY_STARTING,
|
||||
|
@ -34,6 +33,7 @@ from .const import (
|
|||
SERVICE_CHANGE_CHANNEL,
|
||||
SERVICE_SYNC,
|
||||
)
|
||||
from .entity import HarmonyEntity
|
||||
from .subscriber import HarmonyCallback
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -76,28 +76,24 @@ async def async_setup_entry(
|
|||
)
|
||||
|
||||
|
||||
class HarmonyRemote(ConnectionStateMixin, remote.RemoteEntity, RestoreEntity):
|
||||
class HarmonyRemote(HarmonyEntity, remote.RemoteEntity, RestoreEntity):
|
||||
"""Remote representation used to control a Harmony device."""
|
||||
|
||||
def __init__(self, data, activity, delay_secs, out_path):
|
||||
"""Initialize HarmonyRemote class."""
|
||||
super().__init__()
|
||||
self._data = data
|
||||
self._name = data.name
|
||||
super().__init__(data=data)
|
||||
self._state = None
|
||||
self._current_activity = ACTIVITY_POWER_OFF
|
||||
self.default_activity = activity
|
||||
self._activity_starting = None
|
||||
self._is_initial_update = True
|
||||
self.delay_secs = delay_secs
|
||||
self._unique_id = data.unique_id
|
||||
self._last_activity = None
|
||||
self._config_path = out_path
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Supported features for the remote."""
|
||||
return SUPPORT_ACTIVITY
|
||||
self._attr_unique_id = data.unique_id
|
||||
self._attr_device_info = self._data.device_info(DOMAIN)
|
||||
self._attr_name = data.name
|
||||
self._attr_supported_features = SUPPORT_ACTIVITY
|
||||
|
||||
async def _async_update_options(self, data):
|
||||
"""Change options when the options flow does."""
|
||||
|
@ -128,7 +124,7 @@ class HarmonyRemote(ConnectionStateMixin, remote.RemoteEntity, RestoreEntity):
|
|||
"""Complete the initialization."""
|
||||
await super().async_added_to_hass()
|
||||
|
||||
_LOGGER.debug("%s: Harmony Hub added", self._name)
|
||||
_LOGGER.debug("%s: Harmony Hub added", self.name)
|
||||
|
||||
self.async_on_remove(self._clear_disconnection_delay)
|
||||
self._setup_callbacks()
|
||||
|
@ -158,26 +154,6 @@ class HarmonyRemote(ConnectionStateMixin, remote.RemoteEntity, RestoreEntity):
|
|||
|
||||
self._last_activity = last_state.attributes[ATTR_LAST_ACTIVITY]
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return device info."""
|
||||
return self._data.device_info(DOMAIN)
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return the unique id."""
|
||||
return self._unique_id
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the Harmony device's name."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Return the fact that we should not be polled."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def current_activity(self):
|
||||
"""Return the current activity."""
|
||||
|
@ -202,16 +178,11 @@ class HarmonyRemote(ConnectionStateMixin, remote.RemoteEntity, RestoreEntity):
|
|||
"""Return False if PowerOff is the current activity, otherwise True."""
|
||||
return self._current_activity not in [None, "PowerOff"]
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return True if connected to Hub, otherwise False."""
|
||||
return self._data.available
|
||||
|
||||
@callback
|
||||
def async_new_activity(self, activity_info: tuple) -> None:
|
||||
"""Call for updating the current activity."""
|
||||
activity_id, activity_name = activity_info
|
||||
_LOGGER.debug("%s: activity reported as: %s", self._name, activity_name)
|
||||
_LOGGER.debug("%s: activity reported as: %s", self.name, activity_name)
|
||||
self._current_activity = activity_name
|
||||
if self._is_initial_update:
|
||||
self._is_initial_update = False
|
||||
|
@ -227,7 +198,7 @@ class HarmonyRemote(ConnectionStateMixin, remote.RemoteEntity, RestoreEntity):
|
|||
|
||||
async def async_new_config(self, _=None):
|
||||
"""Call for updating the current activity."""
|
||||
_LOGGER.debug("%s: configuration has been updated", self._name)
|
||||
_LOGGER.debug("%s: configuration has been updated", self.name)
|
||||
self.async_new_activity(self._data.current_activity)
|
||||
await self.hass.async_add_executor_job(self.write_config_file)
|
||||
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
"""Support for Harmony Hub select activities."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from homeassistant.components.select import SelectEntity
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.core import callback
|
||||
|
||||
from .const import ACTIVITY_POWER_OFF, DOMAIN, HARMONY_DATA
|
||||
from .data import HarmonyData
|
||||
from .entity import HarmonyEntity
|
||||
from .subscriber import HarmonyCallback
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(hass, entry, async_add_entities):
|
||||
"""Set up harmony activities select."""
|
||||
data = hass.data[DOMAIN][entry.entry_id][HARMONY_DATA]
|
||||
_LOGGER.debug("creating select for %s hub activities", entry.data[CONF_NAME])
|
||||
async_add_entities(
|
||||
[HarmonyActivitySelect(f"{entry.data[CONF_NAME]} Activities", data)]
|
||||
)
|
||||
|
||||
|
||||
class HarmonyActivitySelect(HarmonyEntity, SelectEntity):
|
||||
"""Select representation of a Harmony activities."""
|
||||
|
||||
def __init__(self, name: str, data: HarmonyData) -> None:
|
||||
"""Initialize HarmonyActivitySelect class."""
|
||||
super().__init__(data=data)
|
||||
self._data = data
|
||||
self._attr_unique_id = self._data.unique_id
|
||||
self._attr_device_info = self._data.device_info(DOMAIN)
|
||||
self._attr_name = name
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Return a representative icon."""
|
||||
if not self.available or self.current_option == ACTIVITY_POWER_OFF:
|
||||
return "mdi:remote-tv-off"
|
||||
return "mdi:remote-tv"
|
||||
|
||||
@property
|
||||
def options(self) -> list[str]:
|
||||
"""Return a set of selectable options."""
|
||||
return [ACTIVITY_POWER_OFF] + sorted(self._data.activity_names)
|
||||
|
||||
@property
|
||||
def current_option(self):
|
||||
"""Return the current activity."""
|
||||
_, activity_name = self._data.current_activity
|
||||
return activity_name
|
||||
|
||||
async def async_select_option(self, option: str) -> None:
|
||||
"""Change the current activity."""
|
||||
await self._data.async_start_activity(option)
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Call when entity is added to hass."""
|
||||
|
||||
callbacks = {
|
||||
"connected": self.async_got_connected,
|
||||
"disconnected": self.async_got_disconnected,
|
||||
"activity_starting": self._async_activity_update,
|
||||
"activity_started": self._async_activity_update,
|
||||
"config_updated": None,
|
||||
}
|
||||
|
||||
self.async_on_remove(self._data.async_subscribe(HarmonyCallback(**callbacks)))
|
||||
|
||||
@callback
|
||||
def _async_activity_update(self, activity_info: tuple):
|
||||
self.async_write_ha_state()
|
|
@ -5,9 +5,9 @@ from homeassistant.components.switch import SwitchEntity
|
|||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.core import callback
|
||||
|
||||
from .connection_state import ConnectionStateMixin
|
||||
from .const import DOMAIN, HARMONY_DATA
|
||||
from .data import HarmonyData
|
||||
from .entity import HarmonyEntity
|
||||
from .subscriber import HarmonyCallback
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -27,31 +27,18 @@ async def async_setup_entry(hass, entry, async_add_entities):
|
|||
async_add_entities(switches, True)
|
||||
|
||||
|
||||
class HarmonyActivitySwitch(ConnectionStateMixin, SwitchEntity):
|
||||
class HarmonyActivitySwitch(HarmonyEntity, SwitchEntity):
|
||||
"""Switch representation of a Harmony activity."""
|
||||
|
||||
def __init__(self, name: str, activity: dict, data: HarmonyData) -> None:
|
||||
"""Initialize HarmonyActivitySwitch class."""
|
||||
super().__init__()
|
||||
self._name = name
|
||||
super().__init__(data=data)
|
||||
self._activity_name = activity["label"]
|
||||
self._activity_id = activity["id"]
|
||||
self._data = data
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the Harmony activity's name."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return the unique id."""
|
||||
return f"activity_{self._activity_id}"
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return device info."""
|
||||
return self._data.device_info(DOMAIN)
|
||||
self._attr_entity_registry_enabled_default = False
|
||||
self._attr_unique_id = f"activity_{self._activity_id}"
|
||||
self._attr_name = name
|
||||
self._attr_device_info = self._data.device_info(DOMAIN)
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
|
@ -59,16 +46,6 @@ class HarmonyActivitySwitch(ConnectionStateMixin, SwitchEntity):
|
|||
_, activity_name = self._data.current_activity
|
||||
return activity_name == self._activity_name
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Return that we shouldn't be polled."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return True if we're connected to the Hub, otherwise False."""
|
||||
return self._data.available
|
||||
|
||||
async def async_turn_on(self, **kwargs):
|
||||
"""Start this activity."""
|
||||
await self._data.async_start_activity(self._activity_name)
|
||||
|
|
|
@ -5,6 +5,7 @@ ENTITY_REMOTE = "remote.guest_room"
|
|||
ENTITY_WATCH_TV = "switch.guest_room_watch_tv"
|
||||
ENTITY_PLAY_MUSIC = "switch.guest_room_play_music"
|
||||
ENTITY_NILE_TV = "switch.guest_room_nile_tv"
|
||||
ENTITY_SELECT = "select.guest_room_activities"
|
||||
|
||||
WATCH_TV_ACTIVITY_ID = 123
|
||||
PLAY_MUSIC_ACTIVITY_ID = 456
|
||||
|
|
|
@ -7,6 +7,7 @@ from homeassistant.setup import async_setup_component
|
|||
from .const import (
|
||||
ENTITY_NILE_TV,
|
||||
ENTITY_PLAY_MUSIC,
|
||||
ENTITY_SELECT,
|
||||
ENTITY_WATCH_TV,
|
||||
HUB_NAME,
|
||||
NILE_TV_ACTIVITY_ID,
|
||||
|
@ -55,6 +56,13 @@ async def test_unique_id_migration(mock_hc, hass, mock_write_config):
|
|||
platform="harmony",
|
||||
config_entry_id=entry.entry_id,
|
||||
),
|
||||
# select entity
|
||||
ENTITY_SELECT: er.RegistryEntry(
|
||||
entity_id=ENTITY_SELECT,
|
||||
unique_id=f"{HUB_NAME}_activities",
|
||||
platform="harmony",
|
||||
config_entry_id=entry.entry_id,
|
||||
),
|
||||
},
|
||||
)
|
||||
assert await async_setup_component(hass, DOMAIN, {})
|
||||
|
@ -70,3 +78,6 @@ async def test_unique_id_migration(mock_hc, hass, mock_write_config):
|
|||
|
||||
switch_music = ent_reg.async_get(ENTITY_PLAY_MUSIC)
|
||||
assert switch_music.unique_id == f"activity_{PLAY_MUSIC_ACTIVITY_ID}"
|
||||
|
||||
select_activities = ent_reg.async_get(ENTITY_SELECT)
|
||||
assert select_activities.unique_id == f"{HUB_NAME}_activities"
|
||||
|
|
|
@ -33,7 +33,7 @@ from homeassistant.const import (
|
|||
from homeassistant.util import utcnow
|
||||
|
||||
from .conftest import ACTIVITIES_TO_IDS, TV_DEVICE_ID, TV_DEVICE_NAME
|
||||
from .const import ENTITY_PLAY_MUSIC, ENTITY_REMOTE, ENTITY_WATCH_TV, HUB_NAME
|
||||
from .const import ENTITY_REMOTE, HUB_NAME
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
|
@ -91,10 +91,10 @@ async def test_remote_toggles(mock_hc, hass, mock_write_config):
|
|||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# mocks start with current activity == Watch TV
|
||||
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
|
||||
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON)
|
||||
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
|
||||
# mocks start remote with Watch TV default activity
|
||||
state = hass.states.get(ENTITY_REMOTE)
|
||||
assert state.state == STATE_ON
|
||||
assert state.attributes.get("current_activity") == "Watch TV"
|
||||
|
||||
# turn off remote
|
||||
await hass.services.async_call(
|
||||
|
@ -105,9 +105,9 @@ async def test_remote_toggles(mock_hc, hass, mock_write_config):
|
|||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.is_state(ENTITY_REMOTE, STATE_OFF)
|
||||
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_OFF)
|
||||
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
|
||||
state = hass.states.get(ENTITY_REMOTE)
|
||||
assert state.state == STATE_OFF
|
||||
assert state.attributes.get("current_activity") == "PowerOff"
|
||||
|
||||
# turn on remote, restoring the last activity
|
||||
await hass.services.async_call(
|
||||
|
@ -118,9 +118,9 @@ async def test_remote_toggles(mock_hc, hass, mock_write_config):
|
|||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
|
||||
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON)
|
||||
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
|
||||
state = hass.states.get(ENTITY_REMOTE)
|
||||
assert state.state == STATE_ON
|
||||
assert state.attributes.get("current_activity") == "Watch TV"
|
||||
|
||||
# send new activity command, with activity name
|
||||
await hass.services.async_call(
|
||||
|
@ -131,9 +131,9 @@ async def test_remote_toggles(mock_hc, hass, mock_write_config):
|
|||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
|
||||
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_OFF)
|
||||
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_ON)
|
||||
state = hass.states.get(ENTITY_REMOTE)
|
||||
assert state.state == STATE_ON
|
||||
assert state.attributes.get("current_activity") == "Play Music"
|
||||
|
||||
# send new activity command, with activity id
|
||||
await hass.services.async_call(
|
||||
|
@ -144,9 +144,9 @@ async def test_remote_toggles(mock_hc, hass, mock_write_config):
|
|||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
|
||||
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON)
|
||||
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
|
||||
state = hass.states.get(ENTITY_REMOTE)
|
||||
assert state.state == STATE_ON
|
||||
assert state.attributes.get("current_activity") == "Watch TV"
|
||||
|
||||
|
||||
async def test_async_send_command(mock_hc, harmony_client, hass, mock_write_config):
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
"""Test the Logitech Harmony Hub activity select."""
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from homeassistant.components.harmony.const import DOMAIN
|
||||
from homeassistant.components.select import (
|
||||
ATTR_OPTION,
|
||||
DOMAIN as SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
CONF_HOST,
|
||||
CONF_NAME,
|
||||
STATE_OFF,
|
||||
STATE_ON,
|
||||
STATE_UNAVAILABLE,
|
||||
)
|
||||
from homeassistant.util import utcnow
|
||||
|
||||
from .const import ENTITY_REMOTE, ENTITY_SELECT, HUB_NAME
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
|
||||
async def test_connection_state_changes(
|
||||
harmony_client, mock_hc, hass, mock_write_config
|
||||
):
|
||||
"""Ensure connection changes are reflected in the switch states."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN, data={CONF_HOST: "192.0.2.0", CONF_NAME: HUB_NAME}
|
||||
)
|
||||
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# mocks start with current activity == Watch TV
|
||||
assert hass.states.is_state(ENTITY_SELECT, "Watch TV")
|
||||
|
||||
harmony_client.mock_disconnection()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Entities do not immediately show as unavailable
|
||||
assert hass.states.is_state(ENTITY_SELECT, "Watch TV")
|
||||
|
||||
future_time = utcnow() + timedelta(seconds=10)
|
||||
async_fire_time_changed(hass, future_time)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.is_state(ENTITY_SELECT, STATE_UNAVAILABLE)
|
||||
|
||||
harmony_client.mock_reconnection()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.is_state(ENTITY_SELECT, "Watch TV")
|
||||
|
||||
|
||||
async def test_options(mock_hc, hass, mock_write_config):
|
||||
"""Ensure calls to the switch modify the harmony state."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN, data={CONF_HOST: "192.0.2.0", CONF_NAME: HUB_NAME}
|
||||
)
|
||||
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# assert we have all options
|
||||
state = hass.states.get(ENTITY_SELECT)
|
||||
assert state.attributes.get("options") == [
|
||||
"PowerOff",
|
||||
"Nile-TV",
|
||||
"Play Music",
|
||||
"Watch TV",
|
||||
]
|
||||
|
||||
|
||||
async def test_select_option(mock_hc, hass, mock_write_config):
|
||||
"""Ensure calls to the switch modify the harmony state."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN, data={CONF_HOST: "192.0.2.0", CONF_NAME: HUB_NAME}
|
||||
)
|
||||
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# mocks start with current activity == Watch TV
|
||||
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
|
||||
assert hass.states.is_state(ENTITY_SELECT, "Watch TV")
|
||||
|
||||
# launch Play Music activity
|
||||
await _select_option_and_wait(hass, ENTITY_SELECT, "Play Music")
|
||||
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
|
||||
assert hass.states.is_state(ENTITY_SELECT, "Play Music")
|
||||
|
||||
# turn off harmony by selecting PowerOff activity
|
||||
await _select_option_and_wait(hass, ENTITY_SELECT, "PowerOff")
|
||||
assert hass.states.is_state(ENTITY_REMOTE, STATE_OFF)
|
||||
assert hass.states.is_state(ENTITY_SELECT, "PowerOff")
|
||||
|
||||
|
||||
async def _select_option_and_wait(hass, entity, option):
|
||||
await hass.services.async_call(
|
||||
SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
{
|
||||
ATTR_ENTITY_ID: entity,
|
||||
ATTR_OPTION: option,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
|
@ -16,6 +16,7 @@ from homeassistant.const import (
|
|||
STATE_ON,
|
||||
STATE_UNAVAILABLE,
|
||||
)
|
||||
from homeassistant.helpers import entity_registry
|
||||
from homeassistant.util import utcnow
|
||||
|
||||
from .const import ENTITY_PLAY_MUSIC, ENTITY_REMOTE, ENTITY_WATCH_TV, HUB_NAME
|
||||
|
@ -35,6 +36,17 @@ async def test_connection_state_changes(
|
|||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# check if switch entities are disabled by default
|
||||
assert not hass.states.get(ENTITY_WATCH_TV)
|
||||
assert not hass.states.get(ENTITY_PLAY_MUSIC)
|
||||
|
||||
# enable switch entities
|
||||
ent_reg = entity_registry.async_get(hass)
|
||||
ent_reg.async_update_entity(ENTITY_WATCH_TV, disabled_by=None)
|
||||
ent_reg.async_update_entity(ENTITY_PLAY_MUSIC, disabled_by=None)
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# mocks start with current activity == Watch TV
|
||||
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON)
|
||||
assert hass.states.is_state(ENTITY_PLAY_MUSIC, STATE_OFF)
|
||||
|
@ -78,6 +90,13 @@ async def test_switch_toggles(mock_hc, hass, mock_write_config):
|
|||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# enable switch entities
|
||||
ent_reg = entity_registry.async_get(hass)
|
||||
ent_reg.async_update_entity(ENTITY_WATCH_TV, disabled_by=None)
|
||||
ent_reg.async_update_entity(ENTITY_PLAY_MUSIC, disabled_by=None)
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# mocks start with current activity == Watch TV
|
||||
assert hass.states.is_state(ENTITY_REMOTE, STATE_ON)
|
||||
assert hass.states.is_state(ENTITY_WATCH_TV, STATE_ON)
|
||||
|
|
Loading…
Reference in New Issue