mirror of
https://github.com/home-assistant/core.git
synced 2025-07-14 08:47:10 +00:00
Multiple Hue activate scene (#41353)
This commit is contained in:
parent
1c6d0d138d
commit
366006e0aa
@ -1,4 +1,5 @@
|
|||||||
"""Support for the Philips Hue system."""
|
"""Support for the Philips Hue system."""
|
||||||
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from aiohue.util import normalize_bridge_id
|
from aiohue.util import normalize_bridge_id
|
||||||
@ -7,7 +8,13 @@ from homeassistant import config_entries, core
|
|||||||
from homeassistant.components import persistent_notification
|
from homeassistant.components import persistent_notification
|
||||||
from homeassistant.helpers import device_registry as dr
|
from homeassistant.helpers import device_registry as dr
|
||||||
|
|
||||||
from .bridge import HueBridge
|
from .bridge import (
|
||||||
|
ATTR_GROUP_NAME,
|
||||||
|
ATTR_SCENE_NAME,
|
||||||
|
SCENE_SCHEMA,
|
||||||
|
SERVICE_HUE_SCENE,
|
||||||
|
HueBridge,
|
||||||
|
)
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_ALLOW_HUE_GROUPS,
|
CONF_ALLOW_HUE_GROUPS,
|
||||||
CONF_ALLOW_UNREACHABLE,
|
CONF_ALLOW_UNREACHABLE,
|
||||||
@ -21,6 +28,39 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
async def async_setup(hass, config):
|
async def async_setup(hass, config):
|
||||||
"""Set up the Hue platform."""
|
"""Set up the Hue platform."""
|
||||||
|
|
||||||
|
async def hue_activate_scene(call, skip_reload=True):
|
||||||
|
"""Handle activation of Hue scene."""
|
||||||
|
# Get parameters
|
||||||
|
group_name = call.data[ATTR_GROUP_NAME]
|
||||||
|
scene_name = call.data[ATTR_SCENE_NAME]
|
||||||
|
|
||||||
|
# Call the set scene function on each bridge
|
||||||
|
tasks = [
|
||||||
|
bridge.hue_activate_scene(
|
||||||
|
call, updated=skip_reload, hide_warnings=skip_reload
|
||||||
|
)
|
||||||
|
for bridge in hass.data[DOMAIN].values()
|
||||||
|
if isinstance(bridge, HueBridge)
|
||||||
|
]
|
||||||
|
results = await asyncio.gather(*tasks)
|
||||||
|
|
||||||
|
# Did *any* bridge succeed? If not, refresh / retry
|
||||||
|
# Note that we'll get a "None" value for a successful call
|
||||||
|
if None not in results:
|
||||||
|
if skip_reload:
|
||||||
|
return await hue_activate_scene(call, skip_reload=False)
|
||||||
|
_LOGGER.warning(
|
||||||
|
"No bridge was able to activate " "scene %s in group %s",
|
||||||
|
scene_name,
|
||||||
|
group_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Register a local handler for scene activation
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN, SERVICE_HUE_SCENE, hue_activate_scene, schema=SCENE_SCHEMA
|
||||||
|
)
|
||||||
|
|
||||||
hass.data[DOMAIN] = {}
|
hass.data[DOMAIN] = {}
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -131,4 +171,5 @@ async def async_setup_entry(
|
|||||||
async def async_unload_entry(hass, entry):
|
async def async_unload_entry(hass, entry):
|
||||||
"""Unload a config entry."""
|
"""Unload a config entry."""
|
||||||
bridge = hass.data[DOMAIN].pop(entry.entry_id)
|
bridge = hass.data[DOMAIN].pop(entry.entry_id)
|
||||||
|
hass.services.async_remove(DOMAIN, SERVICE_HUE_SCENE)
|
||||||
return await bridge.async_reset()
|
return await bridge.async_reset()
|
||||||
|
@ -19,7 +19,6 @@ from .const import (
|
|||||||
CONF_ALLOW_UNREACHABLE,
|
CONF_ALLOW_UNREACHABLE,
|
||||||
DEFAULT_ALLOW_HUE_GROUPS,
|
DEFAULT_ALLOW_HUE_GROUPS,
|
||||||
DEFAULT_ALLOW_UNREACHABLE,
|
DEFAULT_ALLOW_UNREACHABLE,
|
||||||
DOMAIN,
|
|
||||||
LOGGER,
|
LOGGER,
|
||||||
)
|
)
|
||||||
from .errors import AuthenticationRequired, CannotConnect
|
from .errors import AuthenticationRequired, CannotConnect
|
||||||
@ -117,10 +116,6 @@ class HueBridge:
|
|||||||
hass.config_entries.async_forward_entry_setup(self.config_entry, "sensor")
|
hass.config_entries.async_forward_entry_setup(self.config_entry, "sensor")
|
||||||
)
|
)
|
||||||
|
|
||||||
hass.services.async_register(
|
|
||||||
DOMAIN, SERVICE_HUE_SCENE, self.hue_activate_scene, schema=SCENE_SCHEMA
|
|
||||||
)
|
|
||||||
|
|
||||||
self.parallel_updates_semaphore = asyncio.Semaphore(
|
self.parallel_updates_semaphore = asyncio.Semaphore(
|
||||||
3 if self.api.config.modelid == "BSB001" else 10
|
3 if self.api.config.modelid == "BSB001" else 10
|
||||||
)
|
)
|
||||||
@ -179,8 +174,6 @@ class HueBridge:
|
|||||||
if self.api is None:
|
if self.api is None:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
self.hass.services.async_remove(DOMAIN, SERVICE_HUE_SCENE)
|
|
||||||
|
|
||||||
while self.reset_jobs:
|
while self.reset_jobs:
|
||||||
self.reset_jobs.pop()()
|
self.reset_jobs.pop()()
|
||||||
|
|
||||||
@ -204,7 +197,7 @@ class HueBridge:
|
|||||||
# None and True are OK
|
# None and True are OK
|
||||||
return False not in results
|
return False not in results
|
||||||
|
|
||||||
async def hue_activate_scene(self, call, updated=False):
|
async def hue_activate_scene(self, call, updated=False, hide_warnings=False):
|
||||||
"""Service to call directly into bridge to set scenes."""
|
"""Service to call directly into bridge to set scenes."""
|
||||||
group_name = call.data[ATTR_GROUP_NAME]
|
group_name = call.data[ATTR_GROUP_NAME]
|
||||||
scene_name = call.data[ATTR_SCENE_NAME]
|
scene_name = call.data[ATTR_SCENE_NAME]
|
||||||
@ -230,18 +223,20 @@ class HueBridge:
|
|||||||
if not updated and (group is None or scene is None):
|
if not updated and (group is None or scene is None):
|
||||||
await self.async_request_call(self.api.groups.update)
|
await self.async_request_call(self.api.groups.update)
|
||||||
await self.async_request_call(self.api.scenes.update)
|
await self.async_request_call(self.api.scenes.update)
|
||||||
await self.hue_activate_scene(call, updated=True)
|
return await self.hue_activate_scene(call, updated=True)
|
||||||
return
|
|
||||||
|
|
||||||
if group is None:
|
if group is None:
|
||||||
LOGGER.warning("Unable to find group %s", group_name)
|
if not hide_warnings:
|
||||||
return
|
LOGGER.warning(
|
||||||
|
"Unable to find group %s" " on bridge %s", group_name, self.host
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
if scene is None:
|
if scene is None:
|
||||||
LOGGER.warning("Unable to find scene %s", scene_name)
|
LOGGER.warning("Unable to find scene %s", scene_name)
|
||||||
return
|
return False
|
||||||
|
|
||||||
await self.async_request_call(partial(group.set_action, scene=scene.id))
|
return await self.async_request_call(partial(group.set_action, scene=scene.id))
|
||||||
|
|
||||||
async def handle_unauthorized_error(self):
|
async def handle_unauthorized_error(self):
|
||||||
"""Create a new config flow when the authorization is no longer valid."""
|
"""Create a new config flow when the authorization is no longer valid."""
|
||||||
|
@ -3,6 +3,7 @@ from collections import deque
|
|||||||
|
|
||||||
from aiohue.groups import Groups
|
from aiohue.groups import Groups
|
||||||
from aiohue.lights import Lights
|
from aiohue.lights import Lights
|
||||||
|
from aiohue.scenes import Scenes
|
||||||
from aiohue.sensors import Sensors
|
from aiohue.sensors import Sensors
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -10,7 +11,7 @@ from homeassistant import config_entries
|
|||||||
from homeassistant.components import hue
|
from homeassistant.components import hue
|
||||||
from homeassistant.components.hue import sensor_base as hue_sensor_base
|
from homeassistant.components.hue import sensor_base as hue_sensor_base
|
||||||
|
|
||||||
from tests.async_mock import Mock, patch
|
from tests.async_mock import AsyncMock, Mock, patch
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
@ -65,6 +66,39 @@ def create_mock_bridge(hass):
|
|||||||
return bridge
|
return bridge
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_api(hass):
|
||||||
|
"""Mock the Hue api."""
|
||||||
|
api = Mock(initialize=AsyncMock())
|
||||||
|
api.mock_requests = []
|
||||||
|
api.mock_light_responses = deque()
|
||||||
|
api.mock_group_responses = deque()
|
||||||
|
api.mock_sensor_responses = deque()
|
||||||
|
api.mock_scene_responses = deque()
|
||||||
|
|
||||||
|
async def mock_request(method, path, **kwargs):
|
||||||
|
kwargs["method"] = method
|
||||||
|
kwargs["path"] = path
|
||||||
|
api.mock_requests.append(kwargs)
|
||||||
|
|
||||||
|
if path == "lights":
|
||||||
|
return api.mock_light_responses.popleft()
|
||||||
|
if path == "groups":
|
||||||
|
return api.mock_group_responses.popleft()
|
||||||
|
if path == "sensors":
|
||||||
|
return api.mock_sensor_responses.popleft()
|
||||||
|
if path == "scenes":
|
||||||
|
return api.mock_scene_responses.popleft()
|
||||||
|
return None
|
||||||
|
|
||||||
|
api.config.apiversion = "9.9.9"
|
||||||
|
api.lights = Lights({}, mock_request)
|
||||||
|
api.groups = Groups({}, mock_request)
|
||||||
|
api.sensors = Sensors({}, mock_request)
|
||||||
|
api.scenes = Scenes({}, mock_request)
|
||||||
|
return api
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_bridge(hass):
|
def mock_bridge(hass):
|
||||||
"""Mock a Hue bridge."""
|
"""Mock a Hue bridge."""
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
"""Test Hue bridge."""
|
"""Test Hue bridge."""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant import config_entries
|
||||||
|
from homeassistant.components import hue
|
||||||
from homeassistant.components.hue import bridge, errors
|
from homeassistant.components.hue import bridge, errors
|
||||||
from homeassistant.components.hue.const import (
|
from homeassistant.components.hue.const import (
|
||||||
CONF_ALLOW_HUE_GROUPS,
|
CONF_ALLOW_HUE_GROUPS,
|
||||||
@ -88,7 +90,7 @@ async def test_reset_unloads_entry_if_setup(hass):
|
|||||||
), patch.object(hass.config_entries, "async_forward_entry_setup") as mock_forward:
|
), patch.object(hass.config_entries, "async_forward_entry_setup") as mock_forward:
|
||||||
assert await hue_bridge.async_setup() is True
|
assert await hue_bridge.async_setup() is True
|
||||||
|
|
||||||
assert len(hass.services.async_services()) == 1
|
assert len(hass.services.async_services()) == 0
|
||||||
assert len(mock_forward.mock_calls) == 3
|
assert len(mock_forward.mock_calls) == 3
|
||||||
|
|
||||||
with patch.object(
|
with patch.object(
|
||||||
@ -120,3 +122,131 @@ async def test_handle_unauthorized(hass):
|
|||||||
assert hue_bridge.authorized is False
|
assert hue_bridge.authorized is False
|
||||||
assert len(mock_create.mock_calls) == 1
|
assert len(mock_create.mock_calls) == 1
|
||||||
assert mock_create.mock_calls[0][1][1] == "1.2.3.4"
|
assert mock_create.mock_calls[0][1][1] == "1.2.3.4"
|
||||||
|
|
||||||
|
|
||||||
|
GROUP_RESPONSE = {
|
||||||
|
"group_1": {
|
||||||
|
"name": "Group 1",
|
||||||
|
"lights": ["1", "2"],
|
||||||
|
"type": "LightGroup",
|
||||||
|
"action": {
|
||||||
|
"on": True,
|
||||||
|
"bri": 254,
|
||||||
|
"hue": 10000,
|
||||||
|
"sat": 254,
|
||||||
|
"effect": "none",
|
||||||
|
"xy": [0.5, 0.5],
|
||||||
|
"ct": 250,
|
||||||
|
"alert": "select",
|
||||||
|
"colormode": "ct",
|
||||||
|
},
|
||||||
|
"state": {"any_on": True, "all_on": False},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SCENE_RESPONSE = {
|
||||||
|
"scene_1": {
|
||||||
|
"name": "Cozy dinner",
|
||||||
|
"lights": ["1", "2"],
|
||||||
|
"owner": "ffffffffe0341b1b376a2389376a2389",
|
||||||
|
"recycle": True,
|
||||||
|
"locked": False,
|
||||||
|
"appdata": {"version": 1, "data": "myAppData"},
|
||||||
|
"picture": "",
|
||||||
|
"lastupdated": "2015-12-03T10:09:22",
|
||||||
|
"version": 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_hue_activate_scene(hass, mock_api):
|
||||||
|
"""Test successful hue_activate_scene."""
|
||||||
|
config_entry = config_entries.ConfigEntry(
|
||||||
|
1,
|
||||||
|
hue.DOMAIN,
|
||||||
|
"Mock Title",
|
||||||
|
{"host": "mock-host", "username": "mock-username"},
|
||||||
|
"test",
|
||||||
|
config_entries.CONN_CLASS_LOCAL_POLL,
|
||||||
|
system_options={},
|
||||||
|
options={CONF_ALLOW_HUE_GROUPS: True, CONF_ALLOW_UNREACHABLE: False},
|
||||||
|
)
|
||||||
|
hue_bridge = bridge.HueBridge(hass, config_entry)
|
||||||
|
|
||||||
|
mock_api.mock_group_responses.append(GROUP_RESPONSE)
|
||||||
|
mock_api.mock_scene_responses.append(SCENE_RESPONSE)
|
||||||
|
|
||||||
|
with patch("aiohue.Bridge", return_value=mock_api), patch.object(
|
||||||
|
hass.config_entries, "async_forward_entry_setup"
|
||||||
|
):
|
||||||
|
assert await hue_bridge.async_setup() is True
|
||||||
|
|
||||||
|
assert hue_bridge.api is mock_api
|
||||||
|
|
||||||
|
call = Mock()
|
||||||
|
call.data = {"group_name": "Group 1", "scene_name": "Cozy dinner"}
|
||||||
|
with patch("aiohue.Bridge", return_value=mock_api):
|
||||||
|
assert await hue_bridge.hue_activate_scene(call) is None
|
||||||
|
|
||||||
|
assert len(mock_api.mock_requests) == 3
|
||||||
|
assert mock_api.mock_requests[2]["json"]["scene"] == "scene_1"
|
||||||
|
assert mock_api.mock_requests[2]["path"] == "groups/group_1/action"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_hue_activate_scene_group_not_found(hass, mock_api):
|
||||||
|
"""Test failed hue_activate_scene due to missing group."""
|
||||||
|
config_entry = config_entries.ConfigEntry(
|
||||||
|
1,
|
||||||
|
hue.DOMAIN,
|
||||||
|
"Mock Title",
|
||||||
|
{"host": "mock-host", "username": "mock-username"},
|
||||||
|
"test",
|
||||||
|
config_entries.CONN_CLASS_LOCAL_POLL,
|
||||||
|
system_options={},
|
||||||
|
options={CONF_ALLOW_HUE_GROUPS: True, CONF_ALLOW_UNREACHABLE: False},
|
||||||
|
)
|
||||||
|
hue_bridge = bridge.HueBridge(hass, config_entry)
|
||||||
|
|
||||||
|
mock_api.mock_group_responses.append({})
|
||||||
|
mock_api.mock_scene_responses.append(SCENE_RESPONSE)
|
||||||
|
|
||||||
|
with patch("aiohue.Bridge", return_value=mock_api), patch.object(
|
||||||
|
hass.config_entries, "async_forward_entry_setup"
|
||||||
|
):
|
||||||
|
assert await hue_bridge.async_setup() is True
|
||||||
|
|
||||||
|
assert hue_bridge.api is mock_api
|
||||||
|
|
||||||
|
call = Mock()
|
||||||
|
call.data = {"group_name": "Group 1", "scene_name": "Cozy dinner"}
|
||||||
|
with patch("aiohue.Bridge", return_value=mock_api):
|
||||||
|
assert await hue_bridge.hue_activate_scene(call) is False
|
||||||
|
|
||||||
|
|
||||||
|
async def test_hue_activate_scene_scene_not_found(hass, mock_api):
|
||||||
|
"""Test failed hue_activate_scene due to missing scene."""
|
||||||
|
config_entry = config_entries.ConfigEntry(
|
||||||
|
1,
|
||||||
|
hue.DOMAIN,
|
||||||
|
"Mock Title",
|
||||||
|
{"host": "mock-host", "username": "mock-username"},
|
||||||
|
"test",
|
||||||
|
config_entries.CONN_CLASS_LOCAL_POLL,
|
||||||
|
system_options={},
|
||||||
|
options={CONF_ALLOW_HUE_GROUPS: True, CONF_ALLOW_UNREACHABLE: False},
|
||||||
|
)
|
||||||
|
hue_bridge = bridge.HueBridge(hass, config_entry)
|
||||||
|
|
||||||
|
mock_api.mock_group_responses.append(GROUP_RESPONSE)
|
||||||
|
mock_api.mock_scene_responses.append({})
|
||||||
|
|
||||||
|
with patch("aiohue.Bridge", return_value=mock_api), patch.object(
|
||||||
|
hass.config_entries, "async_forward_entry_setup"
|
||||||
|
):
|
||||||
|
assert await hue_bridge.async_setup() is True
|
||||||
|
|
||||||
|
assert hue_bridge.api is mock_api
|
||||||
|
|
||||||
|
call = Mock()
|
||||||
|
call.data = {"group_name": "Group 1", "scene_name": "Cozy dinner"}
|
||||||
|
with patch("aiohue.Bridge", return_value=mock_api):
|
||||||
|
assert await hue_bridge.hue_activate_scene(call) is False
|
||||||
|
185
tests/components/hue/test_init_multiple_bridges.py
Normal file
185
tests/components/hue/test_init_multiple_bridges.py
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
"""Test Hue init with multiple bridges."""
|
||||||
|
|
||||||
|
from aiohue.groups import Groups
|
||||||
|
from aiohue.lights import Lights
|
||||||
|
from aiohue.scenes import Scenes
|
||||||
|
from aiohue.sensors import Sensors
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant import config_entries
|
||||||
|
from homeassistant.components import hue
|
||||||
|
from homeassistant.components.hue import sensor_base as hue_sensor_base
|
||||||
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
from tests.async_mock import Mock, patch
|
||||||
|
|
||||||
|
|
||||||
|
async def setup_component(hass):
|
||||||
|
"""Hue component."""
|
||||||
|
with patch.object(hue, "async_setup_entry", return_value=True):
|
||||||
|
assert (
|
||||||
|
await async_setup_component(
|
||||||
|
hass,
|
||||||
|
hue.DOMAIN,
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
is True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_hue_activate_scene_both_responds(
|
||||||
|
hass, mock_bridge1, mock_bridge2, mock_config_entry1, mock_config_entry2
|
||||||
|
):
|
||||||
|
"""Test that makes both bridges successfully activate a scene."""
|
||||||
|
|
||||||
|
await setup_component(hass)
|
||||||
|
|
||||||
|
await setup_bridge(hass, mock_bridge1, mock_config_entry1)
|
||||||
|
await setup_bridge(hass, mock_bridge2, mock_config_entry2)
|
||||||
|
|
||||||
|
with patch.object(
|
||||||
|
mock_bridge1, "hue_activate_scene", return_value=None
|
||||||
|
) as mock_hue_activate_scene1, patch.object(
|
||||||
|
mock_bridge2, "hue_activate_scene", return_value=None
|
||||||
|
) as mock_hue_activate_scene2:
|
||||||
|
await hass.services.async_call(
|
||||||
|
"hue",
|
||||||
|
"hue_activate_scene",
|
||||||
|
{"group_name": "group_2", "scene_name": "my_scene"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_hue_activate_scene1.assert_called_once()
|
||||||
|
mock_hue_activate_scene2.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_hue_activate_scene_one_responds(
|
||||||
|
hass, mock_bridge1, mock_bridge2, mock_config_entry1, mock_config_entry2
|
||||||
|
):
|
||||||
|
"""Test that makes only one bridge successfully activate a scene."""
|
||||||
|
|
||||||
|
await setup_component(hass)
|
||||||
|
|
||||||
|
await setup_bridge(hass, mock_bridge1, mock_config_entry1)
|
||||||
|
await setup_bridge(hass, mock_bridge2, mock_config_entry2)
|
||||||
|
|
||||||
|
with patch.object(
|
||||||
|
mock_bridge1, "hue_activate_scene", return_value=None
|
||||||
|
) as mock_hue_activate_scene1, patch.object(
|
||||||
|
mock_bridge2, "hue_activate_scene", return_value=False
|
||||||
|
) as mock_hue_activate_scene2:
|
||||||
|
await hass.services.async_call(
|
||||||
|
"hue",
|
||||||
|
"hue_activate_scene",
|
||||||
|
{"group_name": "group_2", "scene_name": "my_scene"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_hue_activate_scene1.assert_called_once()
|
||||||
|
mock_hue_activate_scene2.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_hue_activate_scene_zero_responds(
|
||||||
|
hass, mock_bridge1, mock_bridge2, mock_config_entry1, mock_config_entry2
|
||||||
|
):
|
||||||
|
"""Test that makes no bridge successfully activate a scene."""
|
||||||
|
|
||||||
|
await setup_component(hass)
|
||||||
|
|
||||||
|
await setup_bridge(hass, mock_bridge1, mock_config_entry1)
|
||||||
|
await setup_bridge(hass, mock_bridge2, mock_config_entry2)
|
||||||
|
|
||||||
|
with patch.object(
|
||||||
|
mock_bridge1, "hue_activate_scene", return_value=False
|
||||||
|
) as mock_hue_activate_scene1, patch.object(
|
||||||
|
mock_bridge2, "hue_activate_scene", return_value=False
|
||||||
|
) as mock_hue_activate_scene2:
|
||||||
|
await hass.services.async_call(
|
||||||
|
"hue",
|
||||||
|
"hue_activate_scene",
|
||||||
|
{"group_name": "group_2", "scene_name": "my_scene"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# both were retried
|
||||||
|
assert mock_hue_activate_scene1.call_count == 2
|
||||||
|
assert mock_hue_activate_scene2.call_count == 2
|
||||||
|
|
||||||
|
|
||||||
|
async def setup_bridge(hass, mock_bridge, config_entry):
|
||||||
|
"""Load the Hue light platform with the provided bridge."""
|
||||||
|
mock_bridge.config_entry = config_entry
|
||||||
|
hass.data[hue.DOMAIN][config_entry.entry_id] = mock_bridge
|
||||||
|
await hass.config_entries.async_forward_entry_setup(config_entry, "light")
|
||||||
|
# To flush out the service call to update the group
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_config_entry1(hass):
|
||||||
|
"""Mock a config entry."""
|
||||||
|
return create_config_entry()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_config_entry2(hass):
|
||||||
|
"""Mock a config entry."""
|
||||||
|
return create_config_entry()
|
||||||
|
|
||||||
|
|
||||||
|
def create_config_entry():
|
||||||
|
"""Mock a config entry."""
|
||||||
|
return config_entries.ConfigEntry(
|
||||||
|
1,
|
||||||
|
hue.DOMAIN,
|
||||||
|
"Mock Title",
|
||||||
|
{"host": "mock-host"},
|
||||||
|
"test",
|
||||||
|
config_entries.CONN_CLASS_LOCAL_POLL,
|
||||||
|
system_options={},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_bridge1(hass):
|
||||||
|
"""Mock a Hue bridge."""
|
||||||
|
return create_mock_bridge(hass)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_bridge2(hass):
|
||||||
|
"""Mock a Hue bridge."""
|
||||||
|
return create_mock_bridge(hass)
|
||||||
|
|
||||||
|
|
||||||
|
def create_mock_bridge(hass):
|
||||||
|
"""Create a mock Hue bridge."""
|
||||||
|
bridge = Mock(
|
||||||
|
hass=hass,
|
||||||
|
available=True,
|
||||||
|
authorized=True,
|
||||||
|
allow_unreachable=False,
|
||||||
|
allow_groups=False,
|
||||||
|
api=Mock(),
|
||||||
|
reset_jobs=[],
|
||||||
|
spec=hue.HueBridge,
|
||||||
|
)
|
||||||
|
bridge.sensor_manager = hue_sensor_base.SensorManager(bridge)
|
||||||
|
bridge.mock_requests = []
|
||||||
|
|
||||||
|
async def mock_request(method, path, **kwargs):
|
||||||
|
kwargs["method"] = method
|
||||||
|
kwargs["path"] = path
|
||||||
|
bridge.mock_requests.append(kwargs)
|
||||||
|
return {}
|
||||||
|
|
||||||
|
async def async_request_call(task):
|
||||||
|
await task()
|
||||||
|
|
||||||
|
bridge.async_request_call = async_request_call
|
||||||
|
bridge.api.config.apiversion = "9.9.9"
|
||||||
|
bridge.api.lights = Lights({}, mock_request)
|
||||||
|
bridge.api.groups = Groups({}, mock_request)
|
||||||
|
bridge.api.sensors = Sensors({}, mock_request)
|
||||||
|
bridge.api.scenes = Scenes({}, mock_request)
|
||||||
|
return bridge
|
Loading…
x
Reference in New Issue
Block a user