Add cover platform to Dynalite (#32594)

* lib version

* unit-test refactoring

* added type hints

* added cover

* added test to see that consts have the same value as library consts

* Update tests/components/dynalite/test_init.py

Co-Authored-By: Martin Hjelmare <marhje52@gmail.com>

* removed trigger template

* Update homeassistant/components/dynalite/__init__.py

Co-Authored-By: Martin Hjelmare <marhje52@gmail.com>

* Update homeassistant/components/dynalite/const.py

Co-Authored-By: Martin Hjelmare <marhje52@gmail.com>

* removed CONF_TRIGGER from const
corrected type hints - not clear why mypy didn't catch it

* conversion of the config to library CONFs

* moved to use the value since it should come from the library

* taking CONF_HOST from homeassistant.const instead of module const

* use dict.get
removed leftover log

* force device_class to be from homeassistant consts

* move dict.get to inline

* removed CONF from values
changed "channelcover" to "channel_cover"

* moved some CONF values out of const.py and taking them from homeassistant.const

* verifying that device class is a valid HA device class

* moved shutter to home assistant const

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Ziv 2020-04-02 21:26:36 +03:00 committed by GitHub
parent 1d89d22a38
commit 39408ab240
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 587 additions and 106 deletions

View File

@ -1,43 +1,55 @@
"""Support for the Dynalite networks.""" """Support for the Dynalite networks."""
import asyncio import asyncio
from typing import Any, Dict, Union
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.const import CONF_HOST from homeassistant.components.cover import DEVICE_CLASSES_SCHEMA
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT, CONF_TYPE
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
# Loading the config flow file will register the flow # Loading the config flow file will register the flow
from .bridge import DynaliteBridge from .bridge import DynaliteBridge
from .const import ( from .const import (
ACTIVE_INIT,
ACTIVE_OFF,
ACTIVE_ON,
CONF_ACTIVE, CONF_ACTIVE,
CONF_ACTIVE_INIT,
CONF_ACTIVE_OFF,
CONF_ACTIVE_ON,
CONF_AREA, CONF_AREA,
CONF_AUTO_DISCOVER, CONF_AUTO_DISCOVER,
CONF_BRIDGES, CONF_BRIDGES,
CONF_CHANNEL, CONF_CHANNEL,
CONF_CHANNEL_TYPE, CONF_CHANNEL_COVER,
CONF_CLOSE_PRESET,
CONF_DEFAULT, CONF_DEFAULT,
CONF_DEVICE_CLASS,
CONF_DURATION,
CONF_FADE, CONF_FADE,
CONF_NAME,
CONF_NO_DEFAULT, CONF_NO_DEFAULT,
CONF_POLLTIMER, CONF_OPEN_PRESET,
CONF_PORT, CONF_POLL_TIMER,
CONF_PRESET, CONF_PRESET,
CONF_ROOM_OFF,
CONF_ROOM_ON,
CONF_STOP_PRESET,
CONF_TEMPLATE,
CONF_TILT_TIME,
DEFAULT_CHANNEL_TYPE, DEFAULT_CHANNEL_TYPE,
DEFAULT_NAME, DEFAULT_NAME,
DEFAULT_PORT, DEFAULT_PORT,
DEFAULT_TEMPLATES,
DOMAIN, DOMAIN,
ENTITY_PLATFORMS, ENTITY_PLATFORMS,
LOGGER, LOGGER,
) )
def num_string(value): def num_string(value: Union[int, str]) -> str:
"""Test if value is a string of digits, aka an integer.""" """Test if value is a string of digits, aka an integer."""
new_value = str(value) new_value = str(value)
if new_value.isdigit(): if new_value.isdigit():
@ -49,7 +61,7 @@ CHANNEL_DATA_SCHEMA = vol.Schema(
{ {
vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_FADE): vol.Coerce(float), vol.Optional(CONF_FADE): vol.Coerce(float),
vol.Optional(CONF_CHANNEL_TYPE, default=DEFAULT_CHANNEL_TYPE): vol.Any( vol.Optional(CONF_TYPE, default=DEFAULT_CHANNEL_TYPE): vol.Any(
"light", "switch" "light", "switch"
), ),
} }
@ -63,15 +75,66 @@ PRESET_DATA_SCHEMA = vol.Schema(
PRESET_SCHEMA = vol.Schema({num_string: vol.Any(PRESET_DATA_SCHEMA, None)}) PRESET_SCHEMA = vol.Schema({num_string: vol.Any(PRESET_DATA_SCHEMA, None)})
TEMPLATE_ROOM_SCHEMA = vol.Schema(
{vol.Optional(CONF_ROOM_ON): num_string, vol.Optional(CONF_ROOM_OFF): num_string}
)
TEMPLATE_TIMECOVER_SCHEMA = vol.Schema(
{
vol.Optional(CONF_CHANNEL_COVER): num_string,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_OPEN_PRESET): num_string,
vol.Optional(CONF_CLOSE_PRESET): num_string,
vol.Optional(CONF_STOP_PRESET): num_string,
vol.Optional(CONF_DURATION): vol.Coerce(float),
vol.Optional(CONF_TILT_TIME): vol.Coerce(float),
}
)
TEMPLATE_DATA_SCHEMA = vol.Any(TEMPLATE_ROOM_SCHEMA, TEMPLATE_TIMECOVER_SCHEMA)
TEMPLATE_SCHEMA = vol.Schema({str: TEMPLATE_DATA_SCHEMA})
def validate_area(config: Dict[str, Any]) -> Dict[str, Any]:
"""Validate that template parameters are only used if area is using the relevant template."""
conf_set = set()
for template in DEFAULT_TEMPLATES:
for conf in DEFAULT_TEMPLATES[template]:
conf_set.add(conf)
if config.get(CONF_TEMPLATE):
for conf in DEFAULT_TEMPLATES[config[CONF_TEMPLATE]]:
conf_set.remove(conf)
for conf in conf_set:
if config.get(conf):
raise vol.Invalid(
f"{conf} should not be part of area {config[CONF_NAME]} config"
)
return config
AREA_DATA_SCHEMA = vol.Schema( AREA_DATA_SCHEMA = vol.Schema(
{ vol.All(
vol.Required(CONF_NAME): cv.string, {
vol.Optional(CONF_FADE): vol.Coerce(float), vol.Required(CONF_NAME): cv.string,
vol.Optional(CONF_NO_DEFAULT): vol.Coerce(bool), vol.Optional(CONF_TEMPLATE): cv.string,
vol.Optional(CONF_CHANNEL): CHANNEL_SCHEMA, vol.Optional(CONF_FADE): vol.Coerce(float),
vol.Optional(CONF_PRESET): PRESET_SCHEMA, vol.Optional(CONF_NO_DEFAULT): cv.boolean,
}, vol.Optional(CONF_CHANNEL): CHANNEL_SCHEMA,
vol.Optional(CONF_PRESET): PRESET_SCHEMA,
# the next ones can be part of the templates
vol.Optional(CONF_ROOM_ON): num_string,
vol.Optional(CONF_ROOM_OFF): num_string,
vol.Optional(CONF_CHANNEL_COVER): num_string,
vol.Optional(CONF_DEVICE_CLASS): DEVICE_CLASSES_SCHEMA,
vol.Optional(CONF_OPEN_PRESET): num_string,
vol.Optional(CONF_CLOSE_PRESET): num_string,
vol.Optional(CONF_STOP_PRESET): num_string,
vol.Optional(CONF_DURATION): vol.Coerce(float),
vol.Optional(CONF_TILT_TIME): vol.Coerce(float),
},
validate_area,
)
) )
AREA_SCHEMA = vol.Schema({num_string: vol.Any(AREA_DATA_SCHEMA, None)}) AREA_SCHEMA = vol.Schema({num_string: vol.Any(AREA_DATA_SCHEMA, None)})
@ -85,13 +148,14 @@ BRIDGE_SCHEMA = vol.Schema(
vol.Required(CONF_HOST): cv.string, vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): int, vol.Optional(CONF_PORT, default=DEFAULT_PORT): int,
vol.Optional(CONF_AUTO_DISCOVER, default=False): vol.Coerce(bool), vol.Optional(CONF_AUTO_DISCOVER, default=False): vol.Coerce(bool),
vol.Optional(CONF_POLLTIMER, default=1.0): vol.Coerce(float), vol.Optional(CONF_POLL_TIMER, default=1.0): vol.Coerce(float),
vol.Optional(CONF_AREA): AREA_SCHEMA, vol.Optional(CONF_AREA): AREA_SCHEMA,
vol.Optional(CONF_DEFAULT): PLATFORM_DEFAULTS_SCHEMA, vol.Optional(CONF_DEFAULT): PLATFORM_DEFAULTS_SCHEMA,
vol.Optional(CONF_ACTIVE, default=False): vol.Any( vol.Optional(CONF_ACTIVE, default=False): vol.Any(
CONF_ACTIVE_ON, CONF_ACTIVE_OFF, CONF_ACTIVE_INIT, cv.boolean ACTIVE_ON, ACTIVE_OFF, ACTIVE_INIT, cv.boolean
), ),
vol.Optional(CONF_PRESET): PRESET_SCHEMA, vol.Optional(CONF_PRESET): PRESET_SCHEMA,
vol.Optional(CONF_TEMPLATE): TEMPLATE_SCHEMA,
} }
) )
@ -105,7 +169,7 @@ CONFIG_SCHEMA = vol.Schema(
) )
async def async_setup(hass, config): async def async_setup(hass: HomeAssistant, config: Dict[str, Any]) -> bool:
"""Set up the Dynalite platform.""" """Set up the Dynalite platform."""
conf = config.get(DOMAIN) conf = config.get(DOMAIN)
@ -137,7 +201,7 @@ async def async_setup(hass, config):
return True return True
async def async_entry_changed(hass, entry): async def async_entry_changed(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Reload entry since the data has changed.""" """Reload entry since the data has changed."""
LOGGER.debug("Reconfiguring entry %s", entry.data) LOGGER.debug("Reconfiguring entry %s", entry.data)
bridge = hass.data[DOMAIN][entry.entry_id] bridge = hass.data[DOMAIN][entry.entry_id]
@ -145,7 +209,7 @@ async def async_entry_changed(hass, entry):
LOGGER.debug("Reconfiguring entry finished %s", entry.data) LOGGER.debug("Reconfiguring entry finished %s", entry.data)
async def async_setup_entry(hass, entry): async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up a bridge from a config entry.""" """Set up a bridge from a config entry."""
LOGGER.debug("Setting up entry %s", entry.data) LOGGER.debug("Setting up entry %s", entry.data)
bridge = DynaliteBridge(hass, entry.data) bridge = DynaliteBridge(hass, entry.data)
@ -163,7 +227,7 @@ async def async_setup_entry(hass, entry):
return True return True
async def async_unload_entry(hass, entry): async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
LOGGER.debug("Unloading entry %s", entry.data) LOGGER.debug("Unloading entry %s", entry.data)
hass.data[DOMAIN].pop(entry.entry_id) hass.data[DOMAIN].pop(entry.entry_id)

View File

@ -1,17 +1,26 @@
"""Code to handle a Dynalite bridge.""" """Code to handle a Dynalite bridge."""
from typing import TYPE_CHECKING, Any, Callable, Dict, List
from dynalite_devices_lib.dynalite_devices import DynaliteDevices from dynalite_devices_lib.dynalite_devices import DynaliteDevices
from homeassistant.core import callback from homeassistant.const import CONF_HOST
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.dispatcher import async_dispatcher_send
from .const import CONF_ALL, CONF_HOST, ENTITY_PLATFORMS, LOGGER from .const import CONF_ALL, ENTITY_PLATFORMS, LOGGER
from .convert_config import convert_config
if TYPE_CHECKING: # pragma: no cover
from dynalite_devices_lib.dynalite_devices import ( # pylint: disable=ungrouped-imports
DynaliteBaseDevice,
)
class DynaliteBridge: class DynaliteBridge:
"""Manages a single Dynalite bridge.""" """Manages a single Dynalite bridge."""
def __init__(self, hass, config): def __init__(self, hass: HomeAssistant, config: Dict[str, Any]) -> None:
"""Initialize the system based on host parameter.""" """Initialize the system based on host parameter."""
self.hass = hass self.hass = hass
self.area = {} self.area = {}
@ -23,20 +32,20 @@ class DynaliteBridge:
new_device_func=self.add_devices_when_registered, new_device_func=self.add_devices_when_registered,
update_device_func=self.update_device, update_device_func=self.update_device,
) )
self.dynalite_devices.configure(config) self.dynalite_devices.configure(convert_config(config))
async def async_setup(self): async def async_setup(self) -> bool:
"""Set up a Dynalite bridge.""" """Set up a Dynalite bridge."""
# Configure the dynalite devices # Configure the dynalite devices
LOGGER.debug("Setting up bridge - host %s", self.host) LOGGER.debug("Setting up bridge - host %s", self.host)
return await self.dynalite_devices.async_setup() return await self.dynalite_devices.async_setup()
def reload_config(self, config): def reload_config(self, config: Dict[str, Any]) -> None:
"""Reconfigure a bridge when config changes.""" """Reconfigure a bridge when config changes."""
LOGGER.debug("Reloading bridge - host %s, config %s", self.host, config) LOGGER.debug("Reloading bridge - host %s, config %s", self.host, config)
self.dynalite_devices.configure(config) self.dynalite_devices.configure(convert_config(config))
def update_signal(self, device=None): def update_signal(self, device: "DynaliteBaseDevice" = None) -> str:
"""Create signal to use to trigger entity update.""" """Create signal to use to trigger entity update."""
if device: if device:
signal = f"dynalite-update-{self.host}-{device.unique_id}" signal = f"dynalite-update-{self.host}-{device.unique_id}"
@ -45,12 +54,12 @@ class DynaliteBridge:
return signal return signal
@callback @callback
def update_device(self, device): def update_device(self, device: "DynaliteBaseDevice") -> None:
"""Call when a device or all devices should be updated.""" """Call when a device or all devices should be updated."""
if device == CONF_ALL: if device == CONF_ALL:
# This is used to signal connection or disconnection, so all devices may become available or not. # This is used to signal connection or disconnection, so all devices may become available or not.
log_string = ( log_string = (
"Connected" if self.dynalite_devices.available else "Disconnected" "Connected" if self.dynalite_devices.connected else "Disconnected"
) )
LOGGER.info("%s to dynalite host", log_string) LOGGER.info("%s to dynalite host", log_string)
async_dispatcher_send(self.hass, self.update_signal()) async_dispatcher_send(self.hass, self.update_signal())
@ -58,13 +67,13 @@ class DynaliteBridge:
async_dispatcher_send(self.hass, self.update_signal(device)) async_dispatcher_send(self.hass, self.update_signal(device))
@callback @callback
def register_add_devices(self, platform, async_add_devices): def register_add_devices(self, platform: str, async_add_devices: Callable) -> None:
"""Add an async_add_entities for a category.""" """Add an async_add_entities for a category."""
self.async_add_devices[platform] = async_add_devices self.async_add_devices[platform] = async_add_devices
if platform in self.waiting_devices: if platform in self.waiting_devices:
self.async_add_devices[platform](self.waiting_devices[platform]) self.async_add_devices[platform](self.waiting_devices[platform])
def add_devices_when_registered(self, devices): def add_devices_when_registered(self, devices: List["DynaliteBaseDevice"]) -> None:
"""Add the devices to HA if the add devices callback was registered, otherwise queue until it is.""" """Add the devices to HA if the add devices callback was registered, otherwise queue until it is."""
for platform in ENTITY_PLATFORMS: for platform in ENTITY_PLATFORMS:
platform_devices = [ platform_devices = [

View File

@ -1,4 +1,6 @@
"""Config flow to configure Dynalite hub.""" """Config flow to configure Dynalite hub."""
from typing import Any, Dict
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.const import CONF_HOST from homeassistant.const import CONF_HOST
@ -12,11 +14,11 @@ class DynaliteFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
VERSION = 1 VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
def __init__(self): def __init__(self) -> None:
"""Initialize the Dynalite flow.""" """Initialize the Dynalite flow."""
self.host = None self.host = None
async def async_step_import(self, import_info): async def async_step_import(self, import_info: Dict[str, Any]) -> Any:
"""Import a new bridge as a config entry.""" """Import a new bridge as a config entry."""
LOGGER.debug("Starting async_step_import - %s", import_info) LOGGER.debug("Starting async_step_import - %s", import_info)
host = import_info[CONF_HOST] host = import_info[CONF_HOST]

View File

@ -1,31 +1,54 @@
"""Constants for the Dynalite component.""" """Constants for the Dynalite component."""
import logging import logging
from homeassistant.components.cover import DEVICE_CLASS_SHUTTER
from homeassistant.const import CONF_ROOM
LOGGER = logging.getLogger(__package__) LOGGER = logging.getLogger(__package__)
DOMAIN = "dynalite" DOMAIN = "dynalite"
ENTITY_PLATFORMS = ["light", "switch"] ENTITY_PLATFORMS = ["light", "switch", "cover"]
CONF_ACTIVE = "active" CONF_ACTIVE = "active"
CONF_ACTIVE_INIT = "init" ACTIVE_INIT = "init"
CONF_ACTIVE_OFF = "off" ACTIVE_OFF = "off"
CONF_ACTIVE_ON = "on" ACTIVE_ON = "on"
CONF_ALL = "ALL" CONF_ALL = "ALL"
CONF_AREA = "area" CONF_AREA = "area"
CONF_AUTO_DISCOVER = "autodiscover" CONF_AUTO_DISCOVER = "autodiscover"
CONF_BRIDGES = "bridges" CONF_BRIDGES = "bridges"
CONF_CHANNEL = "channel" CONF_CHANNEL = "channel"
CONF_CHANNEL_TYPE = "type" CONF_CHANNEL_COVER = "channel_cover"
CONF_CLOSE_PRESET = "close"
CONF_DEFAULT = "default" CONF_DEFAULT = "default"
CONF_DEVICE_CLASS = "class"
CONF_DURATION = "duration"
CONF_FADE = "fade" CONF_FADE = "fade"
CONF_HOST = "host"
CONF_NAME = "name"
CONF_NO_DEFAULT = "nodefault" CONF_NO_DEFAULT = "nodefault"
CONF_POLLTIMER = "polltimer" CONF_OPEN_PRESET = "open"
CONF_PORT = "port" CONF_POLL_TIMER = "polltimer"
CONF_PRESET = "preset" CONF_PRESET = "preset"
CONF_ROOM_OFF = "room_off"
CONF_ROOM_ON = "room_on"
CONF_STOP_PRESET = "stop"
CONF_TEMPLATE = "template"
CONF_TILT_TIME = "tilt"
CONF_TIME_COVER = "time_cover"
DEFAULT_CHANNEL_TYPE = "light" DEFAULT_CHANNEL_TYPE = "light"
DEFAULT_COVER_CLASS = DEVICE_CLASS_SHUTTER
DEFAULT_NAME = "dynalite" DEFAULT_NAME = "dynalite"
DEFAULT_PORT = 12345 DEFAULT_PORT = 12345
DEFAULT_TEMPLATES = {
CONF_ROOM: [CONF_ROOM_ON, CONF_ROOM_OFF],
CONF_TIME_COVER: [
CONF_CHANNEL_COVER,
CONF_DEVICE_CLASS,
CONF_OPEN_PRESET,
CONF_CLOSE_PRESET,
CONF_STOP_PRESET,
CONF_DURATION,
CONF_TILT_TIME,
],
}

View File

@ -0,0 +1,78 @@
"""Convert the HA config to the dynalite config."""
from typing import Any, Dict
from dynalite_devices_lib import const as dyn_const
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT, CONF_ROOM, CONF_TYPE
from .const import (
ACTIVE_INIT,
ACTIVE_OFF,
ACTIVE_ON,
CONF_ACTIVE,
CONF_AREA,
CONF_AUTO_DISCOVER,
CONF_CHANNEL,
CONF_CHANNEL_COVER,
CONF_CLOSE_PRESET,
CONF_DEFAULT,
CONF_DEVICE_CLASS,
CONF_DURATION,
CONF_FADE,
CONF_NO_DEFAULT,
CONF_OPEN_PRESET,
CONF_POLL_TIMER,
CONF_PRESET,
CONF_ROOM_OFF,
CONF_ROOM_ON,
CONF_STOP_PRESET,
CONF_TEMPLATE,
CONF_TILT_TIME,
CONF_TIME_COVER,
)
CONF_MAP = {
CONF_ACTIVE: dyn_const.CONF_ACTIVE,
ACTIVE_INIT: dyn_const.CONF_ACTIVE_INIT,
ACTIVE_OFF: dyn_const.CONF_ACTIVE_OFF,
ACTIVE_ON: dyn_const.CONF_ACTIVE_ON,
CONF_AREA: dyn_const.CONF_AREA,
CONF_AUTO_DISCOVER: dyn_const.CONF_AUTO_DISCOVER,
CONF_CHANNEL: dyn_const.CONF_CHANNEL,
CONF_CHANNEL_COVER: dyn_const.CONF_CHANNEL_COVER,
CONF_TYPE: dyn_const.CONF_CHANNEL_TYPE,
CONF_CLOSE_PRESET: dyn_const.CONF_CLOSE_PRESET,
CONF_DEFAULT: dyn_const.CONF_DEFAULT,
CONF_DEVICE_CLASS: dyn_const.CONF_DEVICE_CLASS,
CONF_DURATION: dyn_const.CONF_DURATION,
CONF_FADE: dyn_const.CONF_FADE,
CONF_HOST: dyn_const.CONF_HOST,
CONF_NAME: dyn_const.CONF_NAME,
CONF_NO_DEFAULT: dyn_const.CONF_NO_DEFAULT,
CONF_OPEN_PRESET: dyn_const.CONF_OPEN_PRESET,
CONF_POLL_TIMER: dyn_const.CONF_POLL_TIMER,
CONF_PORT: dyn_const.CONF_PORT,
CONF_PRESET: dyn_const.CONF_PRESET,
CONF_ROOM: dyn_const.CONF_ROOM,
CONF_ROOM_OFF: dyn_const.CONF_ROOM_OFF,
CONF_ROOM_ON: dyn_const.CONF_ROOM_ON,
CONF_STOP_PRESET: dyn_const.CONF_STOP_PRESET,
CONF_TEMPLATE: dyn_const.CONF_TEMPLATE,
CONF_TILT_TIME: dyn_const.CONF_TILT_TIME,
CONF_TIME_COVER: dyn_const.CONF_TIME_COVER,
}
def convert_config(config: Dict[str, Any]) -> Dict[str, Any]:
"""Convert a config dict by replacing component consts with library consts."""
result = {}
for (key, value) in config.items():
if isinstance(value, dict):
new_value = convert_config(value)
elif isinstance(value, str):
new_value = CONF_MAP.get(value, value)
else:
new_value = value
result[CONF_MAP.get(key, key)] = new_value
return result

View File

@ -0,0 +1,98 @@
"""Support for the Dynalite channels as covers."""
from typing import Callable
from homeassistant.components.cover import DEVICE_CLASSES, CoverDevice
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from .const import DEFAULT_COVER_CLASS
from .dynalitebase import DynaliteBase, async_setup_entry_base
async def async_setup_entry(
hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: Callable
) -> None:
"""Record the async_add_entities function to add them later when received from Dynalite."""
@callback
def cover_from_device(device, bridge):
if device.has_tilt:
return DynaliteCoverWithTilt(device, bridge)
return DynaliteCover(device, bridge)
async_setup_entry_base(
hass, config_entry, async_add_entities, "cover", cover_from_device
)
class DynaliteCover(DynaliteBase, CoverDevice):
"""Representation of a Dynalite Channel as a Home Assistant Cover."""
@property
def device_class(self) -> str:
"""Return the class of the device."""
dev_cls = self._device.device_class
if dev_cls in DEVICE_CLASSES:
return dev_cls
return DEFAULT_COVER_CLASS
@property
def current_cover_position(self) -> int:
"""Return the position of the cover from 0 to 100."""
return self._device.current_cover_position
@property
def is_opening(self) -> bool:
"""Return true if cover is opening."""
return self._device.is_opening
@property
def is_closing(self) -> bool:
"""Return true if cover is closing."""
return self._device.is_closing
@property
def is_closed(self) -> bool:
"""Return true if cover is closed."""
return self._device.is_closed
async def async_open_cover(self, **kwargs) -> None:
"""Open the cover."""
await self._device.async_open_cover(**kwargs)
async def async_close_cover(self, **kwargs) -> None:
"""Close the cover."""
await self._device.async_close_cover(**kwargs)
async def async_set_cover_position(self, **kwargs) -> None:
"""Set the cover position."""
await self._device.async_set_cover_position(**kwargs)
async def async_stop_cover(self, **kwargs) -> None:
"""Stop the cover."""
await self._device.async_stop_cover(**kwargs)
class DynaliteCoverWithTilt(DynaliteCover):
"""Representation of a Dynalite Channel as a Home Assistant Cover that uses up and down for tilt."""
@property
def current_cover_tilt_position(self) -> int:
"""Return the current tilt position."""
return self._device.current_cover_tilt_position
async def async_open_cover_tilt(self, **kwargs) -> None:
"""Open cover tilt."""
await self._device.async_open_cover_tilt(**kwargs)
async def async_close_cover_tilt(self, **kwargs) -> None:
"""Close cover tilt."""
await self._device.async_close_cover_tilt(**kwargs)
async def async_set_cover_tilt_position(self, **kwargs) -> None:
"""Set the cover tilt position."""
await self._device.async_set_cover_tilt_position(**kwargs)
async def async_stop_cover_tilt(self, **kwargs) -> None:
"""Stop the cover tilt."""
await self._device.async_stop_cover_tilt(**kwargs)

View File

@ -1,5 +1,9 @@
"""Support for the Dynalite devices as entities.""" """Support for the Dynalite devices as entities."""
from homeassistant.core import callback from typing import Any, Callable, Dict
from homeassistant.components.dynalite.bridge import DynaliteBridge
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
@ -7,8 +11,12 @@ from .const import DOMAIN, LOGGER
def async_setup_entry_base( def async_setup_entry_base(
hass, config_entry, async_add_entities, platform, entity_from_device hass: HomeAssistant,
): config_entry: ConfigEntry,
async_add_entities: Callable,
platform: str,
entity_from_device: Callable,
) -> None:
"""Record the async_add_entities function to add them later when received from Dynalite.""" """Record the async_add_entities function to add them later when received from Dynalite."""
LOGGER.debug("Setting up %s entry = %s", platform, config_entry.data) LOGGER.debug("Setting up %s entry = %s", platform, config_entry.data)
bridge = hass.data[DOMAIN][config_entry.entry_id] bridge = hass.data[DOMAIN][config_entry.entry_id]
@ -18,8 +26,7 @@ def async_setup_entry_base(
# assumes it is called with a single platform # assumes it is called with a single platform
added_entities = [] added_entities = []
for device in devices: for device in devices:
if device.category == platform: added_entities.append(entity_from_device(device, bridge))
added_entities.append(entity_from_device(device, bridge))
if added_entities: if added_entities:
async_add_entities(added_entities) async_add_entities(added_entities)
@ -29,29 +36,29 @@ def async_setup_entry_base(
class DynaliteBase(Entity): class DynaliteBase(Entity):
"""Base class for the Dynalite entities.""" """Base class for the Dynalite entities."""
def __init__(self, device, bridge): def __init__(self, device: Any, bridge: DynaliteBridge) -> None:
"""Initialize the base class.""" """Initialize the base class."""
self._device = device self._device = device
self._bridge = bridge self._bridge = bridge
self._unsub_dispatchers = [] self._unsub_dispatchers = []
@property @property
def name(self): def name(self) -> str:
"""Return the name of the entity.""" """Return the name of the entity."""
return self._device.name return self._device.name
@property @property
def unique_id(self): def unique_id(self) -> str:
"""Return the unique ID of the entity.""" """Return the unique ID of the entity."""
return self._device.unique_id return self._device.unique_id
@property @property
def available(self): def available(self) -> bool:
"""Return if entity is available.""" """Return if entity is available."""
return self._device.available return self._device.available
@property @property
def device_info(self): def device_info(self) -> Dict[str, Any]:
"""Device info for this entity.""" """Device info for this entity."""
return { return {
"identifiers": {(DOMAIN, self._device.unique_id)}, "identifiers": {(DOMAIN, self._device.unique_id)},
@ -59,7 +66,7 @@ class DynaliteBase(Entity):
"manufacturer": "Dynalite", "manufacturer": "Dynalite",
} }
async def async_added_to_hass(self): async def async_added_to_hass(self) -> None:
"""Added to hass so need to register to dispatch.""" """Added to hass so need to register to dispatch."""
# register for device specific update # register for device specific update
self._unsub_dispatchers.append( self._unsub_dispatchers.append(
@ -78,7 +85,7 @@ class DynaliteBase(Entity):
) )
) )
async def async_will_remove_from_hass(self): async def async_will_remove_from_hass(self) -> None:
"""Unregister signal dispatch listeners when being removed.""" """Unregister signal dispatch listeners when being removed."""
for unsub in self._unsub_dispatchers: for unsub in self._unsub_dispatchers:
unsub() unsub()

View File

@ -1,10 +1,16 @@
"""Support for Dynalite channels as lights.""" """Support for Dynalite channels as lights."""
from typing import Callable
from homeassistant.components.light import SUPPORT_BRIGHTNESS, Light from homeassistant.components.light import SUPPORT_BRIGHTNESS, Light
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from .dynalitebase import DynaliteBase, async_setup_entry_base from .dynalitebase import DynaliteBase, async_setup_entry_base
async def async_setup_entry(hass, config_entry, async_add_entities): async def async_setup_entry(
hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: Callable
) -> None:
"""Record the async_add_entities function to add them later when received from Dynalite.""" """Record the async_add_entities function to add them later when received from Dynalite."""
async_setup_entry_base( async_setup_entry_base(
@ -16,24 +22,24 @@ class DynaliteLight(DynaliteBase, Light):
"""Representation of a Dynalite Channel as a Home Assistant Light.""" """Representation of a Dynalite Channel as a Home Assistant Light."""
@property @property
def brightness(self): def brightness(self) -> int:
"""Return the brightness of this light between 0..255.""" """Return the brightness of this light between 0..255."""
return self._device.brightness return self._device.brightness
@property @property
def is_on(self): def is_on(self) -> bool:
"""Return true if device is on.""" """Return true if device is on."""
return self._device.is_on return self._device.is_on
async def async_turn_on(self, **kwargs): async def async_turn_on(self, **kwargs) -> None:
"""Turn the light on.""" """Turn the light on."""
await self._device.async_turn_on(**kwargs) await self._device.async_turn_on(**kwargs)
async def async_turn_off(self, **kwargs): async def async_turn_off(self, **kwargs) -> None:
"""Turn the light off.""" """Turn the light off."""
await self._device.async_turn_off(**kwargs) await self._device.async_turn_off(**kwargs)
@property @property
def supported_features(self): def supported_features(self) -> int:
"""Flag supported features.""" """Flag supported features."""
return SUPPORT_BRIGHTNESS return SUPPORT_BRIGHTNESS

View File

@ -5,5 +5,5 @@
"documentation": "https://www.home-assistant.io/integrations/dynalite", "documentation": "https://www.home-assistant.io/integrations/dynalite",
"dependencies": [], "dependencies": [],
"codeowners": ["@ziv1234"], "codeowners": ["@ziv1234"],
"requirements": ["dynalite_devices==0.1.32"] "requirements": ["dynalite_devices==0.1.39"]
} }

View File

@ -1,10 +1,16 @@
"""Support for the Dynalite channels and presets as switches.""" """Support for the Dynalite channels and presets as switches."""
from typing import Callable
from homeassistant.components.switch import SwitchDevice from homeassistant.components.switch import SwitchDevice
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from .dynalitebase import DynaliteBase, async_setup_entry_base from .dynalitebase import DynaliteBase, async_setup_entry_base
async def async_setup_entry(hass, config_entry, async_add_entities): async def async_setup_entry(
hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: Callable
) -> None:
"""Record the async_add_entities function to add them later when received from Dynalite.""" """Record the async_add_entities function to add them later when received from Dynalite."""
async_setup_entry_base( async_setup_entry_base(
@ -16,14 +22,14 @@ class DynaliteSwitch(DynaliteBase, SwitchDevice):
"""Representation of a Dynalite Channel as a Home Assistant Switch.""" """Representation of a Dynalite Channel as a Home Assistant Switch."""
@property @property
def is_on(self): def is_on(self) -> bool:
"""Return true if switch is on.""" """Return true if switch is on."""
return self._device.is_on return self._device.is_on
async def async_turn_on(self, **kwargs): async def async_turn_on(self, **kwargs) -> None:
"""Turn the switch on.""" """Turn the switch on."""
await self._device.async_turn_on() await self._device.async_turn_on()
async def async_turn_off(self, **kwargs): async def async_turn_off(self, **kwargs) -> None:
"""Turn the switch off.""" """Turn the switch off."""
await self._device.async_turn_off() await self._device.async_turn_off()

View File

@ -474,7 +474,7 @@ dsmr_parser==0.18
dweepy==0.3.0 dweepy==0.3.0
# homeassistant.components.dynalite # homeassistant.components.dynalite
dynalite_devices==0.1.32 dynalite_devices==0.1.39
# homeassistant.components.rainforest_eagle # homeassistant.components.rainforest_eagle
eagle200_reader==0.2.4 eagle200_reader==0.2.4

View File

@ -190,7 +190,7 @@ doorbirdpy==2.0.8
dsmr_parser==0.18 dsmr_parser==0.18
# homeassistant.components.dynalite # homeassistant.components.dynalite
dynalite_devices==0.1.32 dynalite_devices==0.1.39
# homeassistant.components.ee_brightbox # homeassistant.components.ee_brightbox
eebrightbox==0.0.4 eebrightbox==0.0.4

View File

@ -44,6 +44,7 @@ async def create_entity_from_device(hass, device):
new_device_func = mock_dyn_dev.mock_calls[1][2]["new_device_func"] new_device_func = mock_dyn_dev.mock_calls[1][2]["new_device_func"]
new_device_func([device]) new_device_func([device])
await hass.async_block_till_done() await hass.async_block_till_done()
return mock_dyn_dev.mock_calls[1][2]["update_device_func"]
async def run_service_tests(hass, device, platform, services): async def run_service_tests(hass, device, platform, services):

View File

@ -61,8 +61,15 @@ async def test_add_devices_then_register(hass):
device2.name = "NAME2" device2.name = "NAME2"
device2.unique_id = "unique2" device2.unique_id = "unique2"
new_device_func([device1, device2]) new_device_func([device1, device2])
device3 = Mock()
device3.category = "switch"
device3.name = "NAME3"
device3.unique_id = "unique3"
new_device_func([device3])
await hass.async_block_till_done() await hass.async_block_till_done()
assert hass.states.get("light.name") assert hass.states.get("light.name")
assert hass.states.get("switch.name2")
assert hass.states.get("switch.name3")
async def test_register_then_add_devices(hass): async def test_register_then_add_devices(hass):
@ -89,3 +96,4 @@ async def test_register_then_add_devices(hass):
new_device_func([device1, device2]) new_device_func([device1, device2])
await hass.async_block_till_done() await hass.async_block_till_done()
assert hass.states.get("light.name") assert hass.states.get("light.name")
assert hass.states.get("switch.name2")

View File

@ -1,6 +1,7 @@
"""Test Dynalite config flow.""" """Test Dynalite config flow."""
from asynctest import CoroutineMock, patch from asynctest import CoroutineMock, patch
import pytest
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.components import dynalite from homeassistant.components import dynalite
@ -8,12 +9,20 @@ from homeassistant.components import dynalite
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
async def run_flow(hass, connection): @pytest.mark.parametrize(
"first_con, second_con,exp_type, exp_result, exp_reason",
[
(True, True, "create_entry", "loaded", ""),
(False, False, "abort", "", "no_connection"),
(True, False, "create_entry", "setup_retry", ""),
],
)
async def test_flow(hass, first_con, second_con, exp_type, exp_result, exp_reason):
"""Run a flow with or without errors and return result.""" """Run a flow with or without errors and return result."""
host = "1.2.3.4" host = "1.2.3.4"
with patch( with patch(
"homeassistant.components.dynalite.bridge.DynaliteDevices.async_setup", "homeassistant.components.dynalite.bridge.DynaliteDevices.async_setup",
side_effect=connection, side_effect=[first_con, second_con],
): ):
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
dynalite.DOMAIN, dynalite.DOMAIN,
@ -21,35 +30,18 @@ async def run_flow(hass, connection):
data={dynalite.CONF_HOST: host}, data={dynalite.CONF_HOST: host},
) )
await hass.async_block_till_done() await hass.async_block_till_done()
return result assert result["type"] == exp_type
if exp_result:
assert result["result"].state == exp_result
async def test_flow_works(hass): if exp_reason:
"""Test a successful config flow.""" assert result["reason"] == exp_reason
result = await run_flow(hass, [True, True])
assert result["type"] == "create_entry"
assert result["result"].state == "loaded"
async def test_flow_setup_fails(hass):
"""Test a flow where async_setup fails."""
result = await run_flow(hass, [False])
assert result["type"] == "abort"
assert result["reason"] == "no_connection"
async def test_flow_setup_fails_in_setup_entry(hass):
"""Test a flow where the initial check works but inside setup_entry, the bridge setup fails."""
result = await run_flow(hass, [True, False])
assert result["type"] == "create_entry"
assert result["result"].state == "setup_retry"
async def test_existing(hass): async def test_existing(hass):
"""Test when the entry exists with the same config.""" """Test when the entry exists with the same config."""
host = "1.2.3.4" host = "1.2.3.4"
MockConfigEntry( MockConfigEntry(
domain=dynalite.DOMAIN, unique_id=host, data={dynalite.CONF_HOST: host} domain=dynalite.DOMAIN, data={dynalite.CONF_HOST: host}
).add_to_hass(hass) ).add_to_hass(hass)
with patch( with patch(
"homeassistant.components.dynalite.bridge.DynaliteDevices.async_setup", "homeassistant.components.dynalite.bridge.DynaliteDevices.async_setup",
@ -81,7 +73,7 @@ async def test_existing_update(hass):
assert await hass.config_entries.async_setup(entry.entry_id) assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
mock_dyn_dev().configure.assert_called_once() mock_dyn_dev().configure.assert_called_once()
assert mock_dyn_dev().configure.mock_calls[0][1][0][dynalite.CONF_PORT] == port1 assert mock_dyn_dev().configure.mock_calls[0][1][0]["port"] == port1
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
dynalite.DOMAIN, dynalite.DOMAIN,
context={"source": config_entries.SOURCE_IMPORT}, context={"source": config_entries.SOURCE_IMPORT},
@ -89,6 +81,26 @@ async def test_existing_update(hass):
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert mock_dyn_dev().configure.call_count == 2 assert mock_dyn_dev().configure.call_count == 2
assert mock_dyn_dev().configure.mock_calls[1][1][0][dynalite.CONF_PORT] == port2 assert mock_dyn_dev().configure.mock_calls[1][1][0]["port"] == port2
assert result["type"] == "abort" assert result["type"] == "abort"
assert result["reason"] == "already_configured" assert result["reason"] == "already_configured"
async def test_two_entries(hass):
"""Test when two different entries exist with different hosts."""
host1 = "1.2.3.4"
host2 = "5.6.7.8"
MockConfigEntry(
domain=dynalite.DOMAIN, data={dynalite.CONF_HOST: host1}
).add_to_hass(hass)
with patch(
"homeassistant.components.dynalite.bridge.DynaliteDevices.async_setup",
return_value=True,
):
result = await hass.config_entries.flow.async_init(
dynalite.DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data={dynalite.CONF_HOST: host2},
)
assert result["type"] == "create_entry"
assert result["result"].state == "loaded"

View File

@ -0,0 +1,100 @@
"""Test Dynalite cover."""
from dynalite_devices_lib.cover import DynaliteTimeCoverWithTiltDevice
import pytest
from .common import (
ATTR_ARGS,
ATTR_METHOD,
ATTR_SERVICE,
create_entity_from_device,
create_mock_device,
run_service_tests,
)
@pytest.fixture
def mock_device():
"""Mock a Dynalite device."""
mock_dev = create_mock_device("cover", DynaliteTimeCoverWithTiltDevice)
mock_dev.device_class = "blind"
return mock_dev
async def test_cover_setup(hass, mock_device):
"""Test a successful setup."""
await create_entity_from_device(hass, mock_device)
entity_state = hass.states.get("cover.name")
assert entity_state.attributes["friendly_name"] == mock_device.name
assert (
entity_state.attributes["current_position"]
== mock_device.current_cover_position
)
assert (
entity_state.attributes["current_tilt_position"]
== mock_device.current_cover_tilt_position
)
assert entity_state.attributes["device_class"] == mock_device.device_class
await run_service_tests(
hass,
mock_device,
"cover",
[
{ATTR_SERVICE: "open_cover", ATTR_METHOD: "async_open_cover"},
{ATTR_SERVICE: "close_cover", ATTR_METHOD: "async_close_cover"},
{ATTR_SERVICE: "stop_cover", ATTR_METHOD: "async_stop_cover"},
{
ATTR_SERVICE: "set_cover_position",
ATTR_METHOD: "async_set_cover_position",
ATTR_ARGS: {"position": 50},
},
{ATTR_SERVICE: "open_cover_tilt", ATTR_METHOD: "async_open_cover_tilt"},
{ATTR_SERVICE: "close_cover_tilt", ATTR_METHOD: "async_close_cover_tilt"},
{ATTR_SERVICE: "stop_cover_tilt", ATTR_METHOD: "async_stop_cover_tilt"},
{
ATTR_SERVICE: "set_cover_tilt_position",
ATTR_METHOD: "async_set_cover_tilt_position",
ATTR_ARGS: {"tilt_position": 50},
},
],
)
async def test_cover_without_tilt(hass, mock_device):
"""Test a cover with no tilt."""
mock_device.has_tilt = False
await create_entity_from_device(hass, mock_device)
await hass.services.async_call(
"cover", "open_cover_tilt", {"entity_id": "cover.name"}, blocking=True
)
await hass.async_block_till_done()
mock_device.async_open_cover_tilt.assert_not_called()
async def check_cover_position(
hass, update_func, device, closing, opening, closed, expected
):
"""Check that a given position behaves correctly."""
device.is_closing = closing
device.is_opening = opening
device.is_closed = closed
update_func(device)
await hass.async_block_till_done()
entity_state = hass.states.get("cover.name")
assert entity_state.state == expected
async def test_cover_positions(hass, mock_device):
"""Test that the state updates in the various positions."""
update_func = await create_entity_from_device(hass, mock_device)
await check_cover_position(
hass, update_func, mock_device, True, False, False, "closing"
)
await check_cover_position(
hass, update_func, mock_device, False, True, False, "opening"
)
await check_cover_position(
hass, update_func, mock_device, False, False, True, "closed"
)
await check_cover_position(
hass, update_func, mock_device, False, False, False, "open"
)

View File

@ -3,7 +3,8 @@
from asynctest import call, patch from asynctest import call, patch
from homeassistant.components import dynalite import homeassistant.components.dynalite.const as dynalite
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PORT, CONF_ROOM
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@ -17,8 +18,7 @@ async def test_empty_config(hass):
async def test_async_setup(hass): async def test_async_setup(hass):
"""Test a successful setup.""" """Test a successful setup with all of the different options."""
host = "1.2.3.4"
with patch( with patch(
"homeassistant.components.dynalite.bridge.DynaliteDevices.async_setup", "homeassistant.components.dynalite.bridge.DynaliteDevices.async_setup",
return_value=True, return_value=True,
@ -30,8 +30,46 @@ async def test_async_setup(hass):
dynalite.DOMAIN: { dynalite.DOMAIN: {
dynalite.CONF_BRIDGES: [ dynalite.CONF_BRIDGES: [
{ {
dynalite.CONF_HOST: host, CONF_HOST: "1.2.3.4",
dynalite.CONF_AREA: {"1": {dynalite.CONF_NAME: "Name"}}, CONF_PORT: 1234,
dynalite.CONF_AUTO_DISCOVER: True,
dynalite.CONF_POLL_TIMER: 5.5,
dynalite.CONF_AREA: {
"1": {
CONF_NAME: "Name1",
dynalite.CONF_CHANNEL: {"4": {}},
dynalite.CONF_NO_DEFAULT: True,
},
"2": {CONF_NAME: "Name2"},
"3": {
CONF_NAME: "Name3",
dynalite.CONF_TEMPLATE: CONF_ROOM,
},
"4": {
CONF_NAME: "Name4",
dynalite.CONF_TEMPLATE: dynalite.CONF_TIME_COVER,
},
},
dynalite.CONF_DEFAULT: {dynalite.CONF_FADE: 2.3},
dynalite.CONF_ACTIVE: dynalite.ACTIVE_INIT,
dynalite.CONF_PRESET: {
"5": {CONF_NAME: "pres5", dynalite.CONF_FADE: 4.5}
},
dynalite.CONF_TEMPLATE: {
CONF_ROOM: {
dynalite.CONF_ROOM_ON: 6,
dynalite.CONF_ROOM_OFF: 7,
},
dynalite.CONF_TIME_COVER: {
dynalite.CONF_OPEN_PRESET: 8,
dynalite.CONF_CLOSE_PRESET: 9,
dynalite.CONF_STOP_PRESET: 10,
dynalite.CONF_CHANNEL_COVER: 3,
dynalite.CONF_DURATION: 2.2,
dynalite.CONF_TILT_TIME: 3.3,
dynalite.CONF_DEVICE_CLASS: "awning",
},
},
} }
] ]
} }
@ -41,6 +79,35 @@ async def test_async_setup(hass):
assert len(hass.config_entries.async_entries(dynalite.DOMAIN)) == 1 assert len(hass.config_entries.async_entries(dynalite.DOMAIN)) == 1
async def test_async_setup_bad_config1(hass):
"""Test a successful with bad config on templates."""
with patch(
"homeassistant.components.dynalite.bridge.DynaliteDevices.async_setup",
return_value=True,
):
assert not await async_setup_component(
hass,
dynalite.DOMAIN,
{
dynalite.DOMAIN: {
dynalite.CONF_BRIDGES: [
{
CONF_HOST: "1.2.3.4",
dynalite.CONF_AREA: {
"1": {
dynalite.CONF_TEMPLATE: dynalite.CONF_TIME_COVER,
CONF_NAME: "Name",
dynalite.CONF_ROOM_ON: 7,
}
},
}
]
}
},
)
await hass.async_block_till_done()
async def test_async_setup_bad_config2(hass): async def test_async_setup_bad_config2(hass):
"""Test a successful with bad config on numbers.""" """Test a successful with bad config on numbers."""
host = "1.2.3.4" host = "1.2.3.4"
@ -55,8 +122,8 @@ async def test_async_setup_bad_config2(hass):
dynalite.DOMAIN: { dynalite.DOMAIN: {
dynalite.CONF_BRIDGES: [ dynalite.CONF_BRIDGES: [
{ {
dynalite.CONF_HOST: host, CONF_HOST: host,
dynalite.CONF_AREA: {"WRONG": {dynalite.CONF_NAME: "Name"}}, dynalite.CONF_AREA: {"WRONG": {CONF_NAME: "Name"}},
} }
] ]
} }
@ -69,7 +136,7 @@ async def test_async_setup_bad_config2(hass):
async def test_unload_entry(hass): async def test_unload_entry(hass):
"""Test being able to unload an entry.""" """Test being able to unload an entry."""
host = "1.2.3.4" host = "1.2.3.4"
entry = MockConfigEntry(domain=dynalite.DOMAIN, data={dynalite.CONF_HOST: host}) entry = MockConfigEntry(domain=dynalite.DOMAIN, data={CONF_HOST: host})
entry.add_to_hass(hass) entry.add_to_hass(hass)
with patch( with patch(
"homeassistant.components.dynalite.bridge.DynaliteDevices.async_setup", "homeassistant.components.dynalite.bridge.DynaliteDevices.async_setup",