Add support for Hue push updates (#50591)
This commit is contained in:
parent
7fd2f8090d
commit
646af533f0
12 changed files with 181 additions and 93 deletions
|
@ -49,7 +49,7 @@ class HueBridge:
|
||||||
# Jobs to be executed when API is reset.
|
# Jobs to be executed when API is reset.
|
||||||
self.reset_jobs = []
|
self.reset_jobs = []
|
||||||
self.sensor_manager = None
|
self.sensor_manager = None
|
||||||
self.unsub_config_entry_listener = None
|
self._update_callbacks = {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def host(self):
|
def host(self):
|
||||||
|
@ -111,9 +111,8 @@ class HueBridge:
|
||||||
3 if self.api.config.modelid == "BSB001" else 10
|
3 if self.api.config.modelid == "BSB001" else 10
|
||||||
)
|
)
|
||||||
|
|
||||||
self.unsub_config_entry_listener = self.config_entry.add_update_listener(
|
self.reset_jobs.append(self.config_entry.add_update_listener(_update_listener))
|
||||||
_update_listener
|
self.reset_jobs.append(asyncio.create_task(self._subscribe_events()).cancel)
|
||||||
)
|
|
||||||
|
|
||||||
self.authorized = True
|
self.authorized = True
|
||||||
return True
|
return True
|
||||||
|
@ -168,8 +167,7 @@ class HueBridge:
|
||||||
while self.reset_jobs:
|
while self.reset_jobs:
|
||||||
self.reset_jobs.pop()()
|
self.reset_jobs.pop()()
|
||||||
|
|
||||||
if self.unsub_config_entry_listener is not None:
|
self._update_callbacks = {}
|
||||||
self.unsub_config_entry_listener()
|
|
||||||
|
|
||||||
# If setup was successful, we set api variable, forwarded entry and
|
# If setup was successful, we set api variable, forwarded entry and
|
||||||
# register service
|
# register service
|
||||||
|
@ -236,6 +234,36 @@ class HueBridge:
|
||||||
self.authorized = False
|
self.authorized = False
|
||||||
create_config_flow(self.hass, self.host)
|
create_config_flow(self.hass, self.host)
|
||||||
|
|
||||||
|
async def _subscribe_events(self):
|
||||||
|
"""Subscribe to Hue events."""
|
||||||
|
try:
|
||||||
|
async for updated_object in self.api.listen_events():
|
||||||
|
key = (updated_object.ITEM_TYPE, updated_object.id)
|
||||||
|
|
||||||
|
if key in self._update_callbacks:
|
||||||
|
self._update_callbacks[key]()
|
||||||
|
|
||||||
|
except GeneratorExit:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@core.callback
|
||||||
|
def listen_updates(self, item_type, item_id, update_callback):
|
||||||
|
"""Listen to updates."""
|
||||||
|
callbacks = self._update_callbacks
|
||||||
|
key = (item_type, item_id)
|
||||||
|
|
||||||
|
if key in callbacks:
|
||||||
|
_LOGGER.warning("Overwriting update callback for %s", key)
|
||||||
|
|
||||||
|
callbacks[key] = update_callback
|
||||||
|
|
||||||
|
@core.callback
|
||||||
|
def unsub():
|
||||||
|
if callbacks.get(key) == update_callback:
|
||||||
|
callbacks.pop(key)
|
||||||
|
|
||||||
|
return unsub
|
||||||
|
|
||||||
|
|
||||||
async def authenticate_bridge(hass: core.HomeAssistant, bridge: aiohue.Bridge):
|
async def authenticate_bridge(hass: core.HomeAssistant, bridge: aiohue.Bridge):
|
||||||
"""Create a bridge object and verify authentication."""
|
"""Create a bridge object and verify authentication."""
|
||||||
|
|
|
@ -39,7 +39,11 @@ class HueEvent(GenericHueDevice):
|
||||||
self.async_update_callback
|
self.async_update_callback
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
_LOGGER.debug("Hue event created: %s", self.event_id)
|
self.bridge.reset_jobs.append(
|
||||||
|
self.bridge.listen_updates(
|
||||||
|
self.sensor.ITEM_TYPE, self.sensor.id, self.async_update_callback
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_callback(self):
|
def async_update_callback(self):
|
||||||
|
|
|
@ -448,6 +448,15 @@ class HueLight(CoordinatorEntity, LightEntity):
|
||||||
|
|
||||||
return info
|
return info
|
||||||
|
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
"""Handle entity being added to Home Assistant."""
|
||||||
|
self.async_on_remove(
|
||||||
|
self.bridge.listen_updates(
|
||||||
|
self.light.ITEM_TYPE, self.light.id, self.async_write_ha_state
|
||||||
|
)
|
||||||
|
)
|
||||||
|
await super().async_added_to_hass()
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs):
|
async def async_turn_on(self, **kwargs):
|
||||||
"""Turn the specified or all lights on."""
|
"""Turn the specified or all lights on."""
|
||||||
command = {"on": True}
|
command = {"on": True}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"name": "Philips Hue",
|
"name": "Philips Hue",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/hue",
|
"documentation": "https://www.home-assistant.io/integrations/hue",
|
||||||
"requirements": ["aiohue==2.3.1"],
|
"requirements": ["aiohue==2.4.2"],
|
||||||
"ssdp": [
|
"ssdp": [
|
||||||
{
|
{
|
||||||
"manufacturer": "Royal Philips Electronics",
|
"manufacturer": "Royal Philips Electronics",
|
||||||
|
|
|
@ -37,9 +37,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
class GenericHueGaugeSensorEntity(GenericZLLSensor, SensorEntity):
|
class GenericHueGaugeSensorEntity(GenericZLLSensor, SensorEntity):
|
||||||
"""Parent class for all 'gauge' Hue device sensors."""
|
"""Parent class for all 'gauge' Hue device sensors."""
|
||||||
|
|
||||||
async def _async_update_ha_state(self, *args, **kwargs):
|
|
||||||
await self.async_update_ha_state(self, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class HueLightLevel(GenericHueGaugeSensorEntity):
|
class HueLightLevel(GenericHueGaugeSensorEntity):
|
||||||
"""The light level sensor entity for a Hue motion sensor device."""
|
"""The light level sensor entity for a Hue motion sensor device."""
|
||||||
|
|
|
@ -166,9 +166,6 @@ class GenericHueSensor(GenericHueDevice, entity.Entity):
|
||||||
|
|
||||||
should_poll = False
|
should_poll = False
|
||||||
|
|
||||||
async def _async_update_ha_state(self, *args, **kwargs):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self):
|
def available(self):
|
||||||
"""Return if sensor is available."""
|
"""Return if sensor is available."""
|
||||||
|
@ -185,6 +182,7 @@ class GenericHueSensor(GenericHueDevice, entity.Entity):
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""When entity is added to hass."""
|
"""When entity is added to hass."""
|
||||||
|
await super().async_added_to_hass()
|
||||||
self.async_on_remove(
|
self.async_on_remove(
|
||||||
self.bridge.sensor_manager.coordinator.async_add_listener(
|
self.bridge.sensor_manager.coordinator.async_add_listener(
|
||||||
self.async_write_ha_state
|
self.async_write_ha_state
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
"""Support for the Philips Hue sensor devices."""
|
"""Support for the Philips Hue sensor devices."""
|
||||||
|
from homeassistant.helpers import entity
|
||||||
|
|
||||||
from .const import DOMAIN as HUE_DOMAIN
|
from .const import DOMAIN as HUE_DOMAIN
|
||||||
|
|
||||||
|
|
||||||
class GenericHueDevice:
|
class GenericHueDevice(entity.Entity):
|
||||||
"""Representation of a Hue device."""
|
"""Representation of a Hue device."""
|
||||||
|
|
||||||
def __init__(self, sensor, name, bridge, primary_sensor=None):
|
def __init__(self, sensor, name, bridge, primary_sensor=None):
|
||||||
|
@ -51,3 +53,12 @@ class GenericHueDevice:
|
||||||
"sw_version": self.primary_sensor.swversion,
|
"sw_version": self.primary_sensor.swversion,
|
||||||
"via_device": (HUE_DOMAIN, self.bridge.api.config.bridgeid),
|
"via_device": (HUE_DOMAIN, self.bridge.api.config.bridgeid),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
"""Handle entity being added to Home Assistant."""
|
||||||
|
self.async_on_remove(
|
||||||
|
self.bridge.listen_updates(
|
||||||
|
self.sensor.ITEM_TYPE, self.sensor.id, self.async_write_ha_state
|
||||||
|
)
|
||||||
|
)
|
||||||
|
await super().async_added_to_hass()
|
||||||
|
|
|
@ -182,7 +182,7 @@ aiohomekit==0.2.61
|
||||||
aiohttp_cors==0.7.0
|
aiohttp_cors==0.7.0
|
||||||
|
|
||||||
# homeassistant.components.hue
|
# homeassistant.components.hue
|
||||||
aiohue==2.3.1
|
aiohue==2.4.2
|
||||||
|
|
||||||
# homeassistant.components.imap
|
# homeassistant.components.imap
|
||||||
aioimaplib==0.7.15
|
aioimaplib==0.7.15
|
||||||
|
|
|
@ -119,7 +119,7 @@ aiohomekit==0.2.61
|
||||||
aiohttp_cors==0.7.0
|
aiohttp_cors==0.7.0
|
||||||
|
|
||||||
# homeassistant.components.hue
|
# homeassistant.components.hue
|
||||||
aiohue==2.3.1
|
aiohue==2.4.2
|
||||||
|
|
||||||
# homeassistant.components.apache_kafka
|
# homeassistant.components.apache_kafka
|
||||||
aiokafka==0.6.0
|
aiokafka==0.6.0
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
"""Test helpers for Hue."""
|
"""Test helpers for Hue."""
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
import logging
|
||||||
from unittest.mock import AsyncMock, Mock, patch
|
from unittest.mock import AsyncMock, Mock, patch
|
||||||
|
|
||||||
from aiohue.groups import Groups
|
from aiohue.groups import Groups
|
||||||
|
@ -30,46 +31,31 @@ def create_mock_bridge(hass):
|
||||||
authorized=True,
|
authorized=True,
|
||||||
allow_unreachable=False,
|
allow_unreachable=False,
|
||||||
allow_groups=False,
|
allow_groups=False,
|
||||||
api=Mock(),
|
api=create_mock_api(hass),
|
||||||
reset_jobs=[],
|
reset_jobs=[],
|
||||||
spec=hue.HueBridge,
|
spec=hue.HueBridge,
|
||||||
)
|
)
|
||||||
bridge.sensor_manager = hue_sensor_base.SensorManager(bridge)
|
bridge.sensor_manager = hue_sensor_base.SensorManager(bridge)
|
||||||
bridge.mock_requests = []
|
bridge.mock_requests = bridge.api.mock_requests
|
||||||
# We're using a deque so we can schedule multiple responses
|
bridge.mock_light_responses = bridge.api.mock_light_responses
|
||||||
# and also means that `popleft()` will blow up if we get more updates
|
bridge.mock_group_responses = bridge.api.mock_group_responses
|
||||||
# than expected.
|
bridge.mock_sensor_responses = bridge.api.mock_sensor_responses
|
||||||
bridge.mock_light_responses = deque()
|
|
||||||
bridge.mock_group_responses = deque()
|
|
||||||
bridge.mock_sensor_responses = deque()
|
|
||||||
|
|
||||||
async def mock_request(method, path, **kwargs):
|
|
||||||
kwargs["method"] = method
|
|
||||||
kwargs["path"] = path
|
|
||||||
bridge.mock_requests.append(kwargs)
|
|
||||||
|
|
||||||
if path == "lights":
|
|
||||||
return bridge.mock_light_responses.popleft()
|
|
||||||
if path == "groups":
|
|
||||||
return bridge.mock_group_responses.popleft()
|
|
||||||
if path == "sensors":
|
|
||||||
return bridge.mock_sensor_responses.popleft()
|
|
||||||
return None
|
|
||||||
|
|
||||||
async def async_request_call(task):
|
async def async_request_call(task):
|
||||||
await task()
|
await task()
|
||||||
|
|
||||||
bridge.async_request_call = async_request_call
|
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)
|
|
||||||
return bridge
|
return bridge
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_api(hass):
|
def mock_api(hass):
|
||||||
"""Mock the Hue api."""
|
"""Mock the Hue api."""
|
||||||
|
return create_mock_api(hass)
|
||||||
|
|
||||||
|
|
||||||
|
def create_mock_api(hass):
|
||||||
|
"""Create a mock API."""
|
||||||
api = Mock(initialize=AsyncMock())
|
api = Mock(initialize=AsyncMock())
|
||||||
api.mock_requests = []
|
api.mock_requests = []
|
||||||
api.mock_light_responses = deque()
|
api.mock_light_responses = deque()
|
||||||
|
@ -92,11 +78,13 @@ def mock_api(hass):
|
||||||
return api.mock_scene_responses.popleft()
|
return api.mock_scene_responses.popleft()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
api.config.apiversion = "9.9.9"
|
api.config.apiversion = "9.9.9"
|
||||||
api.lights = Lights({}, mock_request)
|
api.lights = Lights(logger, {}, mock_request)
|
||||||
api.groups = Groups({}, mock_request)
|
api.groups = Groups(logger, {}, mock_request)
|
||||||
api.sensors = Sensors({}, mock_request)
|
api.sensors = Sensors(logger, {}, mock_request)
|
||||||
api.scenes = Scenes({}, mock_request)
|
api.scenes = Scenes(logger, {}, mock_request)
|
||||||
return api
|
return api
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""Test Hue bridge."""
|
"""Test Hue bridge."""
|
||||||
|
import asyncio
|
||||||
from unittest.mock import AsyncMock, Mock, patch
|
from unittest.mock import AsyncMock, Mock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -12,8 +13,19 @@ from homeassistant.components.hue.const import (
|
||||||
)
|
)
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
|
|
||||||
|
ORIG_SUBSCRIBE_EVENTS = bridge.HueBridge._subscribe_events
|
||||||
|
|
||||||
async def test_bridge_setup(hass):
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def mock_subscribe_events():
|
||||||
|
"""Mock subscribe events method."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.hue.bridge.HueBridge._subscribe_events"
|
||||||
|
) as mock:
|
||||||
|
yield mock
|
||||||
|
|
||||||
|
|
||||||
|
async def test_bridge_setup(hass, mock_subscribe_events):
|
||||||
"""Test a successful setup."""
|
"""Test a successful setup."""
|
||||||
entry = Mock()
|
entry = Mock()
|
||||||
api = Mock(initialize=AsyncMock())
|
api = Mock(initialize=AsyncMock())
|
||||||
|
@ -31,6 +43,8 @@ async def test_bridge_setup(hass):
|
||||||
forward_entries = {c[1][1] for c in mock_forward.mock_calls}
|
forward_entries = {c[1][1] for c in mock_forward.mock_calls}
|
||||||
assert forward_entries == {"light", "binary_sensor", "sensor"}
|
assert forward_entries == {"light", "binary_sensor", "sensor"}
|
||||||
|
|
||||||
|
assert len(mock_subscribe_events.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_bridge_setup_invalid_username(hass):
|
async def test_bridge_setup_invalid_username(hass):
|
||||||
"""Test we start config flow if username is no longer whitelisted."""
|
"""Test we start config flow if username is no longer whitelisted."""
|
||||||
|
@ -78,20 +92,23 @@ async def test_reset_if_entry_had_wrong_auth(hass):
|
||||||
assert await hue_bridge.async_reset()
|
assert await hue_bridge.async_reset()
|
||||||
|
|
||||||
|
|
||||||
async def test_reset_unloads_entry_if_setup(hass):
|
async def test_reset_unloads_entry_if_setup(hass, mock_subscribe_events):
|
||||||
"""Test calling reset while the entry has been setup."""
|
"""Test calling reset while the entry has been setup."""
|
||||||
entry = Mock()
|
entry = Mock()
|
||||||
entry.data = {"host": "1.2.3.4", "username": "mock-username"}
|
entry.data = {"host": "1.2.3.4", "username": "mock-username"}
|
||||||
entry.options = {CONF_ALLOW_HUE_GROUPS: False, CONF_ALLOW_UNREACHABLE: False}
|
entry.options = {CONF_ALLOW_HUE_GROUPS: False, CONF_ALLOW_UNREACHABLE: False}
|
||||||
hue_bridge = bridge.HueBridge(hass, entry)
|
hue_bridge = bridge.HueBridge(hass, entry)
|
||||||
|
|
||||||
with patch.object(bridge, "authenticate_bridge", return_value=Mock()), patch(
|
with patch.object(bridge, "authenticate_bridge"), patch(
|
||||||
"aiohue.Bridge", return_value=Mock()
|
"aiohue.Bridge"
|
||||||
), 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
|
||||||
|
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
|
||||||
assert len(hass.services.async_services()) == 0
|
assert len(hass.services.async_services()) == 0
|
||||||
assert len(mock_forward.mock_calls) == 3
|
assert len(mock_forward.mock_calls) == 3
|
||||||
|
assert len(mock_subscribe_events.mock_calls) == 1
|
||||||
|
|
||||||
with patch.object(
|
with patch.object(
|
||||||
hass.config_entries, "async_forward_entry_unload", return_value=True
|
hass.config_entries, "async_forward_entry_unload", return_value=True
|
||||||
|
@ -109,9 +126,7 @@ async def test_handle_unauthorized(hass):
|
||||||
entry.options = {CONF_ALLOW_HUE_GROUPS: False, CONF_ALLOW_UNREACHABLE: False}
|
entry.options = {CONF_ALLOW_HUE_GROUPS: False, CONF_ALLOW_UNREACHABLE: False}
|
||||||
hue_bridge = bridge.HueBridge(hass, entry)
|
hue_bridge = bridge.HueBridge(hass, entry)
|
||||||
|
|
||||||
with patch.object(bridge, "authenticate_bridge", return_value=Mock()), patch(
|
with patch.object(bridge, "authenticate_bridge"), patch("aiohue.Bridge"):
|
||||||
"aiohue.Bridge", return_value=Mock()
|
|
||||||
):
|
|
||||||
assert await hue_bridge.async_setup() is True
|
assert await hue_bridge.async_setup() is True
|
||||||
|
|
||||||
assert hue_bridge.authorized is True
|
assert hue_bridge.authorized is True
|
||||||
|
@ -282,3 +297,78 @@ async def test_hue_activate_scene_scene_not_found(hass, mock_api):
|
||||||
call.data = {"group_name": "Group 1", "scene_name": "Cozy dinner"}
|
call.data = {"group_name": "Group 1", "scene_name": "Cozy dinner"}
|
||||||
with patch("aiohue.Bridge", return_value=mock_api):
|
with patch("aiohue.Bridge", return_value=mock_api):
|
||||||
assert await hue_bridge.hue_activate_scene(call.data) is False
|
assert await hue_bridge.hue_activate_scene(call.data) is False
|
||||||
|
|
||||||
|
|
||||||
|
async def test_event_updates(hass, caplog):
|
||||||
|
"""Test calling reset while the entry has been setup."""
|
||||||
|
events = asyncio.Queue()
|
||||||
|
|
||||||
|
async def iterate_queue():
|
||||||
|
while True:
|
||||||
|
event = await events.get()
|
||||||
|
if event is None:
|
||||||
|
return
|
||||||
|
yield event
|
||||||
|
|
||||||
|
async def wait_empty_queue():
|
||||||
|
count = 0
|
||||||
|
while not events.empty() and count < 50:
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
hue_bridge = bridge.HueBridge(None, None)
|
||||||
|
hue_bridge.api = Mock(listen_events=iterate_queue)
|
||||||
|
subscription_task = asyncio.create_task(ORIG_SUBSCRIBE_EVENTS(hue_bridge))
|
||||||
|
|
||||||
|
calls = []
|
||||||
|
|
||||||
|
def obj_updated():
|
||||||
|
calls.append(True)
|
||||||
|
|
||||||
|
unsub = hue_bridge.listen_updates("lights", "2", obj_updated)
|
||||||
|
|
||||||
|
events.put_nowait(Mock(ITEM_TYPE="lights", id="1"))
|
||||||
|
|
||||||
|
await wait_empty_queue()
|
||||||
|
assert len(calls) == 0
|
||||||
|
|
||||||
|
events.put_nowait(Mock(ITEM_TYPE="lights", id="2"))
|
||||||
|
|
||||||
|
await wait_empty_queue()
|
||||||
|
assert len(calls) == 1
|
||||||
|
|
||||||
|
unsub()
|
||||||
|
|
||||||
|
events.put_nowait(Mock(ITEM_TYPE="lights", id="2"))
|
||||||
|
|
||||||
|
await wait_empty_queue()
|
||||||
|
assert len(calls) == 1
|
||||||
|
|
||||||
|
# Test we can override update listener.
|
||||||
|
def obj_updated_false():
|
||||||
|
calls.append(False)
|
||||||
|
|
||||||
|
unsub = hue_bridge.listen_updates("lights", "2", obj_updated)
|
||||||
|
unsub_false = hue_bridge.listen_updates("lights", "2", obj_updated_false)
|
||||||
|
|
||||||
|
assert "Overwriting update callback" in caplog.text
|
||||||
|
|
||||||
|
events.put_nowait(Mock(ITEM_TYPE="lights", id="2"))
|
||||||
|
|
||||||
|
await wait_empty_queue()
|
||||||
|
assert len(calls) == 2
|
||||||
|
assert calls[-1] is False
|
||||||
|
|
||||||
|
# Also call multiple times to make sure that works.
|
||||||
|
unsub()
|
||||||
|
unsub()
|
||||||
|
unsub_false()
|
||||||
|
unsub_false()
|
||||||
|
|
||||||
|
events.put_nowait(Mock(ITEM_TYPE="lights", id="2"))
|
||||||
|
|
||||||
|
await wait_empty_queue()
|
||||||
|
assert len(calls) == 2
|
||||||
|
|
||||||
|
events.put_nowait(None)
|
||||||
|
await subscription_task
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
"""Test Hue init with multiple bridges."""
|
"""Test Hue init with multiple bridges."""
|
||||||
from unittest.mock import AsyncMock, Mock, patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from aiohue.groups import Groups
|
|
||||||
from aiohue.lights import Lights
|
|
||||||
from aiohue.scenes import Scenes
|
|
||||||
from aiohue.sensors import Sensors
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components import hue
|
from homeassistant.components import hue
|
||||||
from homeassistant.components.hue import sensor_base as hue_sensor_base
|
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
from .conftest import create_mock_bridge
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
@ -144,37 +141,3 @@ def mock_bridge1(hass):
|
||||||
def mock_bridge2(hass):
|
def mock_bridge2(hass):
|
||||||
"""Mock a Hue bridge."""
|
"""Mock a Hue bridge."""
|
||||||
return create_mock_bridge(hass)
|
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,
|
|
||||||
async_setup=AsyncMock(return_value=True),
|
|
||||||
)
|
|
||||||
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…
Add table
Add a link
Reference in a new issue