1
mirror of https://github.com/home-assistant/core synced 2024-07-30 21:18:57 +02:00

Add slots to the service registry (#95857)

This commit is contained in:
J. Nick Koston 2023-07-05 08:59:36 -05:00 committed by GitHub
parent bd7057f7b1
commit ea57f78392
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 77 additions and 156 deletions

View File

@ -1726,6 +1726,8 @@ class ServiceCall:
class ServiceRegistry: class ServiceRegistry:
"""Offer the services over the eventbus.""" """Offer the services over the eventbus."""
__slots__ = ("_services", "_hass")
def __init__(self, hass: HomeAssistant) -> None: def __init__(self, hass: HomeAssistant) -> None:
"""Initialize a service registry.""" """Initialize a service registry."""
self._services: dict[str, dict[str, Service]] = {} self._services: dict[str, dict[str, Service]] = {}

View File

@ -1,7 +1,7 @@
"""Test Google Smart Home.""" """Test Google Smart Home."""
import asyncio import asyncio
from types import SimpleNamespace from types import SimpleNamespace
from unittest.mock import ANY, call, patch from unittest.mock import ANY, patch
import pytest import pytest
from pytest_unordered import unordered from pytest_unordered import unordered
@ -488,76 +488,41 @@ async def test_execute(
events = async_capture_events(hass, EVENT_COMMAND_RECEIVED) events = async_capture_events(hass, EVENT_COMMAND_RECEIVED)
service_events = async_capture_events(hass, EVENT_CALL_SERVICE) service_events = async_capture_events(hass, EVENT_CALL_SERVICE)
with patch.object( result = await sh.async_handle_message(
hass.services, "async_call", wraps=hass.services.async_call hass,
) as call_service_mock: MockConfig(should_report_state=report_state),
result = await sh.async_handle_message( None,
hass, {
MockConfig(should_report_state=report_state), "requestId": REQ_ID,
None, "inputs": [
{ {
"requestId": REQ_ID, "intent": "action.devices.EXECUTE",
"inputs": [ "payload": {
{ "commands": [
"intent": "action.devices.EXECUTE", {
"payload": { "devices": [
"commands": [ {"id": "light.non_existing"},
{ {"id": "light.ceiling_lights"},
"devices": [ {"id": "light.kitchen_lights"},
{"id": "light.non_existing"}, ],
{"id": "light.ceiling_lights"}, "execution": [
{"id": "light.kitchen_lights"}, {
], "command": "action.devices.commands.OnOff",
"execution": [ "params": {"on": True},
{ },
"command": "action.devices.commands.OnOff", {
"params": {"on": True}, "command": "action.devices.commands.BrightnessAbsolute",
}, "params": {"brightness": 20},
{ },
"command": "action.devices.commands.BrightnessAbsolute", ],
"params": {"brightness": 20}, }
}, ]
], },
} }
] ],
}, },
} const.SOURCE_CLOUD,
], )
},
const.SOURCE_CLOUD,
)
assert call_service_mock.call_count == 4
expected_calls = [
call(
"light",
"turn_on",
{"entity_id": "light.ceiling_lights"},
blocking=not report_state,
context=ANY,
),
call(
"light",
"turn_on",
{"entity_id": "light.kitchen_lights"},
blocking=not report_state,
context=ANY,
),
call(
"light",
"turn_on",
{"entity_id": "light.ceiling_lights", "brightness_pct": 20},
blocking=not report_state,
context=ANY,
),
call(
"light",
"turn_on",
{"entity_id": "light.kitchen_lights", "brightness_pct": 20},
blocking=not report_state,
context=ANY,
),
]
call_service_mock.assert_has_awaits(expected_calls, any_order=True)
await hass.async_block_till_done() await hass.async_block_till_done()
assert result == { assert result == {
@ -682,11 +647,7 @@ async def test_execute_times_out(
# Make DemoLigt.async_turn_on hang waiting for the turn_on_wait event # Make DemoLigt.async_turn_on hang waiting for the turn_on_wait event
await turn_on_wait.wait() await turn_on_wait.wait()
with patch.object( with patch.object(DemoLight, "async_turn_on", wraps=slow_turn_on):
hass.services, "async_call", wraps=hass.services.async_call
) as call_service_mock, patch.object(
DemoLight, "async_turn_on", wraps=slow_turn_on
):
result = await sh.async_handle_message( result = await sh.async_handle_message(
hass, hass,
MockConfig(should_report_state=report_state), MockConfig(should_report_state=report_state),
@ -722,51 +683,10 @@ async def test_execute_times_out(
}, },
const.SOURCE_CLOUD, const.SOURCE_CLOUD,
) )
# Only the two first calls are executed
assert call_service_mock.call_count == 2
expected_calls = [
call(
"light",
"turn_on",
{"entity_id": "light.ceiling_lights"},
blocking=not report_state,
context=ANY,
),
call(
"light",
"turn_on",
{"entity_id": "light.kitchen_lights"},
blocking=not report_state,
context=ANY,
),
]
call_service_mock.assert_has_awaits(expected_calls, any_order=True)
turn_on_wait.set() turn_on_wait.set()
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.async_block_till_done() await hass.async_block_till_done()
# The remaining two calls should now have executed
assert call_service_mock.call_count == 4
expected_calls.extend(
[
call(
"light",
"turn_on",
{"entity_id": "light.ceiling_lights", "brightness_pct": 20},
blocking=not report_state,
context=ANY,
),
call(
"light",
"turn_on",
{"entity_id": "light.kitchen_lights", "brightness_pct": 20},
blocking=not report_state,
context=ANY,
),
]
)
call_service_mock.assert_has_awaits(expected_calls, any_order=True)
await hass.async_block_till_done()
assert result == { assert result == {
"requestId": REQ_ID, "requestId": REQ_ID,

View File

@ -1,5 +1,4 @@
"""The tests for the Group Light platform.""" """The tests for the Group Light platform."""
import unittest.mock
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
import async_timeout import async_timeout
@ -16,7 +15,6 @@ from homeassistant.components.light import (
ATTR_COLOR_TEMP_KELVIN, ATTR_COLOR_TEMP_KELVIN,
ATTR_EFFECT, ATTR_EFFECT,
ATTR_EFFECT_LIST, ATTR_EFFECT_LIST,
ATTR_FLASH,
ATTR_HS_COLOR, ATTR_HS_COLOR,
ATTR_MAX_COLOR_TEMP_KELVIN, ATTR_MAX_COLOR_TEMP_KELVIN,
ATTR_MIN_COLOR_TEMP_KELVIN, ATTR_MIN_COLOR_TEMP_KELVIN,
@ -26,7 +24,6 @@ from homeassistant.components.light import (
ATTR_SUPPORTED_COLOR_MODES, ATTR_SUPPORTED_COLOR_MODES,
ATTR_TRANSITION, ATTR_TRANSITION,
ATTR_WHITE, ATTR_WHITE,
ATTR_XY_COLOR,
DOMAIN as LIGHT_DOMAIN, DOMAIN as LIGHT_DOMAIN,
SERVICE_TOGGLE, SERVICE_TOGGLE,
SERVICE_TURN_OFF, SERVICE_TURN_OFF,
@ -39,16 +36,17 @@ from homeassistant.components.light import (
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
ATTR_SUPPORTED_FEATURES, ATTR_SUPPORTED_FEATURES,
EVENT_CALL_SERVICE,
STATE_OFF, STATE_OFF,
STATE_ON, STATE_ON,
STATE_UNAVAILABLE, STATE_UNAVAILABLE,
STATE_UNKNOWN, STATE_UNKNOWN,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import Event, HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from tests.common import get_fixture_path from tests.common import async_capture_events, get_fixture_path
async def test_default_state(hass: HomeAssistant) -> None: async def test_default_state(hass: HomeAssistant) -> None:
@ -1443,6 +1441,7 @@ async def test_invalid_service_calls(hass: HomeAssistant) -> None:
await group.async_setup_platform( await group.async_setup_platform(
hass, {"name": "test", "entities": ["light.test1", "light.test2"]}, add_entities hass, {"name": "test", "entities": ["light.test1", "light.test2"]}, add_entities
) )
await async_setup_component(hass, "light", {})
await hass.async_block_till_done() await hass.async_block_till_done()
await hass.async_start() await hass.async_start()
await hass.async_block_till_done() await hass.async_block_till_done()
@ -1451,35 +1450,38 @@ async def test_invalid_service_calls(hass: HomeAssistant) -> None:
grouped_light = add_entities.call_args[0][0][0] grouped_light = add_entities.call_args[0][0][0]
grouped_light.hass = hass grouped_light.hass = hass
with unittest.mock.patch.object(hass.services, "async_call") as mock_call: service_call_events = async_capture_events(hass, EVENT_CALL_SERVICE)
await grouped_light.async_turn_on(brightness=150, four_oh_four="404")
data = {ATTR_ENTITY_ID: ["light.test1", "light.test2"], ATTR_BRIGHTNESS: 150}
mock_call.assert_called_once_with(
LIGHT_DOMAIN, SERVICE_TURN_ON, data, blocking=True, context=None
)
mock_call.reset_mock()
await grouped_light.async_turn_off(transition=4, four_oh_four="404") await grouped_light.async_turn_on(brightness=150, four_oh_four="404")
data = {ATTR_ENTITY_ID: ["light.test1", "light.test2"], ATTR_TRANSITION: 4} data = {ATTR_ENTITY_ID: ["light.test1", "light.test2"], ATTR_BRIGHTNESS: 150}
mock_call.assert_called_once_with( assert len(service_call_events) == 1
LIGHT_DOMAIN, SERVICE_TURN_OFF, data, blocking=True, context=None service_event_call: Event = service_call_events[0]
) assert service_event_call.data["domain"] == LIGHT_DOMAIN
mock_call.reset_mock() assert service_event_call.data["service"] == SERVICE_TURN_ON
assert service_event_call.data["service_data"] == data
service_call_events.clear()
data = { await grouped_light.async_turn_off(transition=4, four_oh_four="404")
ATTR_BRIGHTNESS: 150, data = {ATTR_ENTITY_ID: ["light.test1", "light.test2"], ATTR_TRANSITION: 4}
ATTR_XY_COLOR: (0.5, 0.42), assert len(service_call_events) == 1
ATTR_RGB_COLOR: (80, 120, 50), service_event_call: Event = service_call_events[0]
ATTR_COLOR_TEMP_KELVIN: 1234, assert service_event_call.data["domain"] == LIGHT_DOMAIN
ATTR_EFFECT: "Sunshine", assert service_event_call.data["service"] == SERVICE_TURN_OFF
ATTR_TRANSITION: 4, assert service_event_call.data["service_data"] == data
ATTR_FLASH: "long", service_call_events.clear()
}
await grouped_light.async_turn_on(**data) data = {
data[ATTR_ENTITY_ID] = ["light.test1", "light.test2"] ATTR_BRIGHTNESS: 150,
mock_call.assert_called_once_with( ATTR_COLOR_TEMP_KELVIN: 1234,
LIGHT_DOMAIN, SERVICE_TURN_ON, data, blocking=True, context=None ATTR_TRANSITION: 4,
) }
await grouped_light.async_turn_on(**data)
data[ATTR_ENTITY_ID] = ["light.test1", "light.test2"]
service_event_call: Event = service_call_events[0]
assert service_event_call.data["domain"] == LIGHT_DOMAIN
assert service_event_call.data["service"] == SERVICE_TURN_ON
assert service_event_call.data["service_data"] == data
service_call_events.clear()
async def test_reload(hass: HomeAssistant) -> None: async def test_reload(hass: HomeAssistant) -> None:

View File

@ -33,15 +33,12 @@ async def test_async_setup_entry__not_login(
hass.config_entries, "async_forward_entry_setup" hass.config_entries, "async_forward_entry_setup"
) as setup_mock, patch( ) as setup_mock, patch(
"homeassistant.components.vesync.async_process_devices" "homeassistant.components.vesync.async_process_devices"
) as process_mock, patch.object( ) as process_mock:
hass.services, "async_register"
) as register_mock:
assert not await async_setup_entry(hass, config_entry) assert not await async_setup_entry(hass, config_entry)
await hass.async_block_till_done() await hass.async_block_till_done()
assert setups_mock.call_count == 0 assert setups_mock.call_count == 0
assert setup_mock.call_count == 0 assert setup_mock.call_count == 0
assert process_mock.call_count == 0 assert process_mock.call_count == 0
assert register_mock.call_count == 0
assert manager.login.call_count == 1 assert manager.login.call_count == 1
assert DOMAIN not in hass.data assert DOMAIN not in hass.data

View File

@ -332,8 +332,8 @@ def zha_device_mock(
@pytest.fixture @pytest.fixture
def hass_disable_services(hass): def hass_disable_services(hass):
"""Mock service register.""" """Mock services."""
with patch.object(hass.services, "async_register"), patch.object( with patch.object(
hass.services, "has_service", return_value=True hass, "services", MagicMock(has_service=MagicMock(return_value=True))
): ):
yield hass yield hass