Add Deako integration (#121132)

* Deako integration using pydeako

* fix: address feedback

- make unit tests more e2e
- use runtime_data to store connection

* fix: address feedback part 2

- added better type safety for Deako config entries
- refactored the config flow tests to use a conftest mock instead of directly patching
- removed pytest.mark.asyncio test decorators

* fix: address feedback pt 3

- simplify config entry type
- add test for single_instance_allowed
- remove light.py get_state(), only used once, no need to be separate function

* fix: ruff format

* Update homeassistant/components/deako/__init__.py

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
Blake Bryant
2024-08-28 10:16:05 -07:00
committed by GitHub
parent 2dce876a86
commit c049129147
20 changed files with 817 additions and 0 deletions

View File

@ -139,6 +139,7 @@ homeassistant.components.cpuspeed.*
homeassistant.components.crownstone.*
homeassistant.components.date.*
homeassistant.components.datetime.*
homeassistant.components.deako.*
homeassistant.components.deconz.*
homeassistant.components.default_config.*
homeassistant.components.demo.*

View File

@ -294,6 +294,8 @@ build.json @home-assistant/supervisor
/tests/components/date/ @home-assistant/core
/homeassistant/components/datetime/ @home-assistant/core
/tests/components/datetime/ @home-assistant/core
/homeassistant/components/deako/ @sebirdman @balake @deakolights
/tests/components/deako/ @sebirdman @balake @deakolights
/homeassistant/components/debugpy/ @frenck
/tests/components/debugpy/ @frenck
/homeassistant/components/deconz/ @Kane610

View File

@ -0,0 +1,59 @@
"""The deako integration."""
from __future__ import annotations
import logging
from pydeako.deako import Deako, DeviceListTimeout, FindDevicesTimeout
from pydeako.discover import DeakoDiscoverer
from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
_LOGGER: logging.Logger = logging.getLogger(__name__)
PLATFORMS: list[Platform] = [Platform.LIGHT]
type DeakoConfigEntry = ConfigEntry[Deako]
async def async_setup_entry(hass: HomeAssistant, entry: DeakoConfigEntry) -> bool:
"""Set up deako."""
_zc = await zeroconf.async_get_instance(hass)
discoverer = DeakoDiscoverer(_zc)
connection = Deako(discoverer.get_address)
await connection.connect()
try:
await connection.find_devices()
except DeviceListTimeout as exc: # device list never received
_LOGGER.warning("Device not responding to device list")
await connection.disconnect()
raise ConfigEntryNotReady(exc) from exc
except FindDevicesTimeout as exc: # total devices expected not received
_LOGGER.warning("Device not responding to device requests")
await connection.disconnect()
raise ConfigEntryNotReady(exc) from exc
# If deako devices are advertising on mdns, we should be able to get at least one device
devices = connection.get_devices()
if len(devices) == 0:
await connection.disconnect()
raise ConfigEntryNotReady(devices)
entry.runtime_data = connection
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: DeakoConfigEntry) -> bool:
"""Unload a config entry."""
await entry.runtime_data.disconnect()
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

View File

@ -0,0 +1,26 @@
"""Config flow for deako."""
from pydeako.discover import DeakoDiscoverer, DevicesNotFoundException
from homeassistant.components import zeroconf
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_entry_flow
from .const import DOMAIN, NAME
async def _async_has_devices(hass: HomeAssistant) -> bool:
"""Return if there are devices that can be discovered."""
_zc = await zeroconf.async_get_instance(hass)
discoverer = DeakoDiscoverer(_zc)
try:
await discoverer.get_address()
except DevicesNotFoundException:
return False
else:
# address exists, there's at least one device
return True
config_entry_flow.register_discovery_flow(DOMAIN, NAME, _async_has_devices)

View File

@ -0,0 +1,5 @@
"""Constants for Deako."""
# Base component constants
NAME = "Deako"
DOMAIN = "deako"

View File

@ -0,0 +1,96 @@
"""Binary sensor platform for integration_blueprint."""
from typing import Any
from pydeako.deako import Deako
from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import DeakoConfigEntry
from .const import DOMAIN
# Model names
MODEL_SMART = "smart"
MODEL_DIMMER = "dimmer"
async def async_setup_entry(
hass: HomeAssistant,
config: DeakoConfigEntry,
add_entities: AddEntitiesCallback,
) -> None:
"""Configure the platform."""
client = config.runtime_data
add_entities([DeakoLightEntity(client, uuid) for uuid in client.get_devices()])
class DeakoLightEntity(LightEntity):
"""Deako LightEntity class."""
_attr_has_entity_name = True
_attr_name = None
_attr_is_on = False
_attr_available = True
client: Deako
def __init__(self, client: Deako, uuid: str) -> None:
"""Save connection reference."""
self.client = client
self._attr_unique_id = uuid
dimmable = client.is_dimmable(uuid)
model = MODEL_SMART
self._attr_color_mode = ColorMode.ONOFF
if dimmable:
model = MODEL_DIMMER
self._attr_color_mode = ColorMode.BRIGHTNESS
self._attr_supported_color_modes = {self._attr_color_mode}
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, uuid)},
name=client.get_name(uuid),
manufacturer="Deako",
model=model,
)
client.set_state_callback(uuid, self.on_update)
self.update() # set initial state
def on_update(self) -> None:
"""State update callback."""
self.update()
self.schedule_update_ha_state()
async def control_device(self, power: bool, dim: int | None = None) -> None:
"""Control entity state via client."""
assert self._attr_unique_id is not None
await self.client.control_device(self._attr_unique_id, power, dim)
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on the light."""
dim = None
if ATTR_BRIGHTNESS in kwargs:
dim = round(kwargs[ATTR_BRIGHTNESS] / 2.55, 0)
await self.control_device(True, dim)
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off the device."""
await self.control_device(False)
def update(self) -> None:
"""Call to update state."""
assert self._attr_unique_id is not None
state = self.client.get_state(self._attr_unique_id) or {}
self._attr_is_on = bool(state.get("power", False))
if (
self._attr_supported_color_modes is not None
and ColorMode.BRIGHTNESS in self._attr_supported_color_modes
):
self._attr_brightness = int(round(state.get("dim", 0) * 2.55))

View File

@ -0,0 +1,13 @@
{
"domain": "deako",
"name": "Deako",
"codeowners": ["@sebirdman", "@balake", "@deakolights"],
"config_flow": true,
"dependencies": ["zeroconf"],
"documentation": "https://www.home-assistant.io/integrations/deako",
"iot_class": "local_polling",
"loggers": ["pydeako"],
"requirements": ["pydeako==0.4.0"],
"single_config_entry": true,
"zeroconf": ["_deako._tcp.local."]
}

View File

@ -0,0 +1,13 @@
{
"config": {
"step": {
"confirm": {
"description": "Please confirm setting up the Deako integration"
}
},
"abort": {
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]"
}
}
}

View File

@ -114,6 +114,7 @@ FLOWS = {
"cpuspeed",
"crownstone",
"daikin",
"deako",
"deconz",
"deluge",
"denonavr",

View File

@ -1091,6 +1091,13 @@
"config_flow": false,
"iot_class": "local_polling"
},
"deako": {
"name": "Deako",
"integration_type": "hub",
"config_flow": true,
"iot_class": "local_polling",
"single_config_entry": true
},
"debugpy": {
"name": "Remote Python Debugger",
"integration_type": "service",

View File

@ -423,6 +423,11 @@ ZEROCONF = {
"domain": "forked_daapd",
},
],
"_deako._tcp.local.": [
{
"domain": "deako",
},
],
"_devialet-http._tcp.local.": [
{
"domain": "devialet",

View File

@ -1145,6 +1145,16 @@ disallow_untyped_defs = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.deako.*]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.deconz.*]
check_untyped_defs = true
disallow_incomplete_defs = true

View File

@ -1803,6 +1803,9 @@ pydaikin==2.13.4
# homeassistant.components.danfoss_air
pydanfossair==0.1.0
# homeassistant.components.deako
pydeako==0.4.0
# homeassistant.components.deconz
pydeconz==116

View File

@ -1447,6 +1447,9 @@ pycsspeechtts==1.0.8
# homeassistant.components.daikin
pydaikin==2.13.4
# homeassistant.components.deako
pydeako==0.4.0
# homeassistant.components.deconz
pydeconz==116

View File

@ -0,0 +1 @@
"""Tests for the Deako integration."""

View File

@ -0,0 +1,45 @@
"""deako session fixtures."""
from collections.abc import Generator
from unittest.mock import MagicMock, patch
import pytest
from homeassistant.components.deako.const import DOMAIN
from tests.common import MockConfigEntry
@pytest.fixture
def mock_config_entry() -> MockConfigEntry:
"""Return the default mocked config entry."""
return MockConfigEntry(
domain=DOMAIN,
)
@pytest.fixture(autouse=True)
def pydeako_deako_mock() -> Generator[MagicMock]:
"""Mock pydeako deako client."""
with patch("homeassistant.components.deako.Deako", autospec=True) as mock:
yield mock
@pytest.fixture(autouse=True)
def pydeako_discoverer_mock(mock_async_zeroconf: MagicMock) -> Generator[MagicMock]:
"""Mock pydeako discovery client."""
with (
patch("homeassistant.components.deako.DeakoDiscoverer", autospec=True) as mock,
patch("homeassistant.components.deako.config_flow.DeakoDiscoverer", new=mock),
):
yield mock
@pytest.fixture
def mock_deako_setup() -> Generator[MagicMock]:
"""Mock async_setup_entry for config flow tests."""
with patch(
"homeassistant.components.deako.async_setup_entry",
return_value=True,
) as mock_setup:
yield mock_setup

View File

@ -0,0 +1,168 @@
# serializer version: 1
# name: test_dimmable_light_props[light.kitchen-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'supported_color_modes': list([
<ColorMode.BRIGHTNESS: 'brightness'>,
]),
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'light',
'entity_category': None,
'entity_id': 'light.kitchen',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'deako',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'uuid',
'unit_of_measurement': None,
})
# ---
# name: test_dimmable_light_props[light.kitchen-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'brightness': 127,
'color_mode': <ColorMode.BRIGHTNESS: 'brightness'>,
'friendly_name': 'kitchen',
'supported_color_modes': list([
<ColorMode.BRIGHTNESS: 'brightness'>,
]),
'supported_features': <LightEntityFeature: 0>,
}),
'context': <ANY>,
'entity_id': 'light.kitchen',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_light_initial_props[light.kitchen-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'supported_color_modes': list([
<ColorMode.ONOFF: 'onoff'>,
]),
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'light',
'entity_category': None,
'entity_id': 'light.kitchen',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'deako',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'uuid',
'unit_of_measurement': None,
})
# ---
# name: test_light_initial_props[light.kitchen-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'color_mode': None,
'friendly_name': 'kitchen',
'supported_color_modes': list([
<ColorMode.ONOFF: 'onoff'>,
]),
'supported_features': <LightEntityFeature: 0>,
}),
'context': <ANY>,
'entity_id': 'light.kitchen',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
# name: test_light_setup_with_device[light.some_device-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'supported_color_modes': list([
<ColorMode.BRIGHTNESS: 'brightness'>,
]),
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'light',
'entity_category': None,
'entity_id': 'light.some_device',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': None,
'platform': 'deako',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': None,
'unique_id': 'some_device',
'unit_of_measurement': None,
})
# ---
# name: test_light_setup_with_device[light.some_device-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'brightness': 1,
'color_mode': <ColorMode.BRIGHTNESS: 'brightness'>,
'friendly_name': 'some device',
'supported_color_modes': list([
<ColorMode.BRIGHTNESS: 'brightness'>,
]),
'supported_features': <LightEntityFeature: 0>,
}),
'context': <ANY>,
'entity_id': 'light.some_device',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---

View File

@ -0,0 +1,80 @@
"""Tests for the deako component config flow."""
from unittest.mock import MagicMock
from pydeako.discover import DevicesNotFoundException
from homeassistant.components.deako.const import DOMAIN
from homeassistant.config_entries import SOURCE_USER
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from tests.common import MockConfigEntry
async def test_found(
hass: HomeAssistant,
pydeako_discoverer_mock: MagicMock,
mock_deako_setup: MagicMock,
) -> None:
"""Test finding a Deako device."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
# Confirmation form
assert result["type"] is FlowResultType.FORM
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
pydeako_discoverer_mock.return_value.get_address.assert_called_once()
mock_deako_setup.assert_called_once()
async def test_not_found(
hass: HomeAssistant,
pydeako_discoverer_mock: MagicMock,
mock_deako_setup: MagicMock,
) -> None:
"""Test not finding any Deako devices."""
pydeako_discoverer_mock.return_value.get_address.side_effect = (
DevicesNotFoundException()
)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
# Confirmation form
assert result["type"] is FlowResultType.FORM
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "no_devices_found"
pydeako_discoverer_mock.return_value.get_address.assert_called_once()
mock_deako_setup.assert_not_called()
async def test_already_configured(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_deako_setup: MagicMock,
) -> None:
"""Test flow aborts when already configured."""
mock_config_entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "single_instance_allowed"
mock_deako_setup.assert_not_called()

View File

@ -0,0 +1,87 @@
"""Tests for the deako component init."""
from unittest.mock import MagicMock
from pydeako.deako import DeviceListTimeout, FindDevicesTimeout
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
async def test_deako_async_setup_entry(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
pydeako_deako_mock: MagicMock,
pydeako_discoverer_mock: MagicMock,
) -> None:
"""Test successful setup entry."""
pydeako_deako_mock.return_value.get_devices.return_value = {
"id1": {},
"id2": {},
}
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
pydeako_deako_mock.assert_called_once_with(
pydeako_discoverer_mock.return_value.get_address
)
pydeako_deako_mock.return_value.connect.assert_called_once()
pydeako_deako_mock.return_value.find_devices.assert_called_once()
pydeako_deako_mock.return_value.get_devices.assert_called()
assert mock_config_entry.runtime_data == pydeako_deako_mock.return_value
async def test_deako_async_setup_entry_device_list_timeout(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
pydeako_deako_mock: MagicMock,
pydeako_discoverer_mock: MagicMock,
) -> None:
"""Test async_setup_entry raises ConfigEntryNotReady when pydeako raises DeviceListTimeout."""
mock_config_entry.add_to_hass(hass)
pydeako_deako_mock.return_value.find_devices.side_effect = DeviceListTimeout()
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
pydeako_deako_mock.assert_called_once_with(
pydeako_discoverer_mock.return_value.get_address
)
pydeako_deako_mock.return_value.connect.assert_called_once()
pydeako_deako_mock.return_value.find_devices.assert_called_once()
pydeako_deako_mock.return_value.disconnect.assert_called_once()
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
async def test_deako_async_setup_entry_find_devices_timeout(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
pydeako_deako_mock: MagicMock,
pydeako_discoverer_mock: MagicMock,
) -> None:
"""Test async_setup_entry raises ConfigEntryNotReady when pydeako raises FindDevicesTimeout."""
mock_config_entry.add_to_hass(hass)
pydeako_deako_mock.return_value.find_devices.side_effect = FindDevicesTimeout()
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
pydeako_deako_mock.assert_called_once_with(
pydeako_discoverer_mock.return_value.get_address
)
pydeako_deako_mock.return_value.connect.assert_called_once()
pydeako_deako_mock.return_value.find_devices.assert_called_once()
pydeako_deako_mock.return_value.disconnect.assert_called_once()
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY

View File

@ -0,0 +1,192 @@
"""Tests for the light module."""
from unittest.mock import MagicMock
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.light import ATTR_BRIGHTNESS, DOMAIN as LIGHT_DOMAIN
from homeassistant.const import ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from tests.common import MockConfigEntry, snapshot_platform
async def test_light_setup_with_device(
hass: HomeAssistant,
pydeako_deako_mock: MagicMock,
mock_config_entry: MockConfigEntry,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test light platform setup with device returned."""
mock_config_entry.add_to_hass(hass)
pydeako_deako_mock.return_value.get_devices.return_value = {
"some_device": {},
}
pydeako_deako_mock.return_value.get_name.return_value = "some device"
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
async def test_light_initial_props(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
pydeako_deako_mock: MagicMock,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test on/off light is setup with accurate initial properties."""
mock_config_entry.add_to_hass(hass)
pydeako_deako_mock.return_value.get_devices.return_value = {
"uuid": {
"name": "kitchen",
}
}
pydeako_deako_mock.return_value.get_name.return_value = "kitchen"
pydeako_deako_mock.return_value.get_state.return_value = {
"power": False,
}
pydeako_deako_mock.return_value.is_dimmable.return_value = False
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
async def test_dimmable_light_props(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
pydeako_deako_mock: MagicMock,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
) -> None:
"""Test dimmable on/off light is setup with accurate initial properties."""
mock_config_entry.add_to_hass(hass)
pydeako_deako_mock.return_value.get_devices.return_value = {
"uuid": {
"name": "kitchen",
}
}
pydeako_deako_mock.return_value.get_name.return_value = "kitchen"
pydeako_deako_mock.return_value.get_state.return_value = {
"power": True,
"dim": 50,
}
pydeako_deako_mock.return_value.is_dimmable.return_value = True
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
async def test_light_power_change_on(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
pydeako_deako_mock: MagicMock,
) -> None:
"""Test turing on a deako device."""
mock_config_entry.add_to_hass(hass)
pydeako_deako_mock.return_value.get_devices.return_value = {
"uuid": {
"name": "kitchen",
}
}
pydeako_deako_mock.return_value.get_name.return_value = "kitchen"
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: "light.kitchen"},
blocking=True,
)
pydeako_deako_mock.return_value.control_device.assert_called_once_with(
"uuid", True, None
)
async def test_light_power_change_off(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
pydeako_deako_mock: MagicMock,
) -> None:
"""Test turing off a deako device."""
mock_config_entry.add_to_hass(hass)
pydeako_deako_mock.return_value.get_devices.return_value = {
"uuid": {
"name": "kitchen",
}
}
pydeako_deako_mock.return_value.get_name.return_value = "kitchen"
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: "light.kitchen"},
blocking=True,
)
pydeako_deako_mock.return_value.control_device.assert_called_once_with(
"uuid", False, None
)
@pytest.mark.parametrize(
("dim_input", "expected_dim_value"),
[
(3, 1),
(255, 100),
(127, 50),
],
)
async def test_light_brightness_change(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
pydeako_deako_mock: MagicMock,
dim_input: int,
expected_dim_value: int,
) -> None:
"""Test turing on a deako device."""
mock_config_entry.add_to_hass(hass)
pydeako_deako_mock.return_value.get_devices.return_value = {
"uuid": {
"name": "kitchen",
}
}
pydeako_deako_mock.return_value.get_name.return_value = "kitchen"
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{
ATTR_ENTITY_ID: "light.kitchen",
ATTR_BRIGHTNESS: dim_input,
},
blocking=True,
)
pydeako_deako_mock.return_value.control_device.assert_called_once_with(
"uuid", True, expected_dim_value
)