Enhance Dynalite Integration after review (#31760)
* fixes per Martin Hjelmare * pylint fix * final fixes per request * fixed unit tests for new config flow * Added unit-tests to increase coverage. at 97% now * Added unit tests for 100% coverage of component * removed configured_host function and updated config_flow unit tests * added a pylint directive since it tells me by mistake DOMAIN is not used * fixed path (removed __init__) * Update homeassistant/components/dynalite/light.py Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/dynalite/light.py Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * fixed the test as we moved from schedule_update_... to async_schedule * Update homeassistant/components/dynalite/bridge.py Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * removed context from config_flow changed test_init to use the core methods * moved test_light to also use the core interfaces * moved to config_entries.async_unload * additional fixes for the tests * pylint fix and removed unnecessary code * Update tests/components/dynalite/test_light.py Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * Update tests/components/dynalite/test_light.py Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * Update tests/components/dynalite/test_light.py Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * Update tests/components/dynalite/test_light.py Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * Update tests/components/dynalite/test_light.py Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * Update tests/components/dynalite/test_light.py Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * Update tests/components/dynalite/test_light.py Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * Update tests/components/dynalite/test_light.py Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * Update tests/components/dynalite/test_light.py Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * added break in loop * removed last mock_coro reference pylint fix * added coverage for try_connect * added check for a successful connection before bridge.async_setup succeeds also added a "nowait" config option (default False) that avoids this check * changed log level * fixed accidental chmod I did * fixed accidental change * not storing config in bridge * not patching asyncio * moved CONFIG_SCHEMA into component * moved all logs to start capitalized (and revised some of them) * moved test_config_flow to not patch the DynaliteBridge * also took DynaliteBridge patching out of test_init * removed NO_WAIT * fixes to SCHEMA * changed _ in multi-word CONF moved imports to component const.py * removed tries * removed redundant tests * fixed some small change i broke in the library. only version update * fixed rewuirements * Update tests/components/dynalite/test_config_flow.py Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * Update tests/components/dynalite/test_light.py Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * Update tests/components/dynalite/test_config_flow.py Co-Authored-By: Martin Hjelmare <marhje52@gmail.com> * removed HIDDEN_ENTITY removed hass in test fixture * black fixes * removed final piece of hidden_entity from light fix in the library updated config flow so if the entry is already set but with a different config, calls async_update_entry * removed DATA_CONFIGS - no longer necessary * pylint fixes * added coverage * use abort in config_flow * test update * removed logs * test that update actually updates the entry Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
9cc47ca737
commit
36db302cc8
12 changed files with 442 additions and 413 deletions
|
@ -1,24 +1,75 @@
|
|||
"""Support for the Dynalite networks."""
|
||||
from dynalite_devices_lib import BRIDGE_CONFIG_SCHEMA
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_HOST
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
|
||||
# Loading the config flow file will register the flow
|
||||
from .bridge import DynaliteBridge
|
||||
from .config_flow import configured_hosts
|
||||
from .const import CONF_BRIDGES, DATA_CONFIGS, DOMAIN, LOGGER
|
||||
from .const import (
|
||||
CONF_ACTIVE,
|
||||
CONF_AREA,
|
||||
CONF_AUTO_DISCOVER,
|
||||
CONF_BRIDGES,
|
||||
CONF_CHANNEL,
|
||||
CONF_DEFAULT,
|
||||
CONF_FADE,
|
||||
CONF_NAME,
|
||||
CONF_POLLTIMER,
|
||||
CONF_PORT,
|
||||
DEFAULT_NAME,
|
||||
DEFAULT_PORT,
|
||||
DOMAIN,
|
||||
LOGGER,
|
||||
)
|
||||
|
||||
|
||||
def num_string(value):
|
||||
"""Test if value is a string of digits, aka an integer."""
|
||||
new_value = str(value)
|
||||
if new_value.isdigit():
|
||||
return new_value
|
||||
raise vol.Invalid("Not a string with numbers")
|
||||
|
||||
|
||||
CHANNEL_DATA_SCHEMA = vol.Schema(
|
||||
{vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_FADE): vol.Coerce(float)}
|
||||
)
|
||||
|
||||
CHANNEL_SCHEMA = vol.Schema({num_string: CHANNEL_DATA_SCHEMA})
|
||||
|
||||
AREA_DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_NAME): cv.string,
|
||||
vol.Optional(CONF_FADE): vol.Coerce(float),
|
||||
vol.Optional(CONF_CHANNEL): CHANNEL_SCHEMA,
|
||||
},
|
||||
)
|
||||
|
||||
AREA_SCHEMA = vol.Schema({num_string: vol.Any(AREA_DATA_SCHEMA, None)})
|
||||
|
||||
PLATFORM_DEFAULTS_SCHEMA = vol.Schema({vol.Optional(CONF_FADE): vol.Coerce(float)})
|
||||
|
||||
|
||||
BRIDGE_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_PORT, default=DEFAULT_PORT): int,
|
||||
vol.Optional(CONF_AUTO_DISCOVER, default=False): vol.Coerce(bool),
|
||||
vol.Optional(CONF_POLLTIMER, default=1.0): vol.Coerce(float),
|
||||
vol.Optional(CONF_AREA): AREA_SCHEMA,
|
||||
vol.Optional(CONF_DEFAULT): PLATFORM_DEFAULTS_SCHEMA,
|
||||
vol.Optional(CONF_ACTIVE, default=False): vol.Coerce(bool),
|
||||
}
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
DOMAIN: vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_BRIDGES): vol.All(
|
||||
cv.ensure_list, [BRIDGE_CONFIG_SCHEMA]
|
||||
)
|
||||
}
|
||||
{vol.Optional(CONF_BRIDGES): vol.All(cv.ensure_list, [BRIDGE_SCHEMA])}
|
||||
)
|
||||
},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
|
@ -35,9 +86,6 @@ async def async_setup(hass, config):
|
|||
conf = {}
|
||||
|
||||
hass.data[DOMAIN] = {}
|
||||
hass.data[DOMAIN][DATA_CONFIGS] = {}
|
||||
|
||||
configured = configured_hosts(hass)
|
||||
|
||||
# User has configured bridges
|
||||
if CONF_BRIDGES not in conf:
|
||||
|
@ -47,20 +95,13 @@ async def async_setup(hass, config):
|
|||
|
||||
for bridge_conf in bridges:
|
||||
host = bridge_conf[CONF_HOST]
|
||||
LOGGER.debug("async_setup host=%s conf=%s", host, bridge_conf)
|
||||
|
||||
# Store config in hass.data so the config entry can find it
|
||||
hass.data[DOMAIN][DATA_CONFIGS][host] = bridge_conf
|
||||
|
||||
if host in configured:
|
||||
LOGGER.debug("async_setup host=%s already configured", host)
|
||||
continue
|
||||
LOGGER.debug("Starting config entry flow host=%s conf=%s", host, bridge_conf)
|
||||
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={CONF_HOST: bridge_conf[CONF_HOST]},
|
||||
data=bridge_conf,
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -69,25 +110,29 @@ async def async_setup(hass, config):
|
|||
|
||||
async def async_setup_entry(hass, entry):
|
||||
"""Set up a bridge from a config entry."""
|
||||
LOGGER.debug("__init async_setup_entry %s", entry.data)
|
||||
host = entry.data[CONF_HOST]
|
||||
config = hass.data[DOMAIN][DATA_CONFIGS].get(host)
|
||||
LOGGER.debug("Setting up entry %s", entry.data)
|
||||
|
||||
if config is None:
|
||||
LOGGER.error("__init async_setup_entry empty config for host %s", host)
|
||||
return False
|
||||
|
||||
bridge = DynaliteBridge(hass, entry)
|
||||
bridge = DynaliteBridge(hass, entry.data)
|
||||
|
||||
if not await bridge.async_setup():
|
||||
LOGGER.error("bridge.async_setup failed")
|
||||
LOGGER.error("Could not set up bridge for entry %s", entry.data)
|
||||
return False
|
||||
|
||||
if not await bridge.try_connection():
|
||||
LOGGER.errot("Could not connect with entry %s", entry)
|
||||
raise ConfigEntryNotReady
|
||||
|
||||
hass.data[DOMAIN][entry.entry_id] = bridge
|
||||
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(entry, "light")
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass, entry):
|
||||
"""Unload a config entry."""
|
||||
LOGGER.error("async_unload_entry %s", entry.data)
|
||||
bridge = hass.data[DOMAIN].pop(entry.entry_id)
|
||||
return await bridge.async_reset()
|
||||
LOGGER.debug("Unloading entry %s", entry.data)
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
result = await hass.config_entries.async_forward_entry_unload(entry, "light")
|
||||
return result
|
||||
|
|
|
@ -1,118 +1,82 @@
|
|||
"""Code to handle a Dynalite bridge."""
|
||||
|
||||
import asyncio
|
||||
|
||||
from dynalite_devices_lib import DynaliteDevices
|
||||
from dynalite_lib import CONF_ALL
|
||||
|
||||
from homeassistant.const import CONF_HOST
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
|
||||
from .const import DATA_CONFIGS, DOMAIN, LOGGER
|
||||
from .light import DynaliteLight
|
||||
from .const import CONF_ALL, CONF_HOST, LOGGER
|
||||
|
||||
|
||||
class BridgeError(Exception):
|
||||
"""Class to throw exceptions from DynaliteBridge."""
|
||||
|
||||
def __init__(self, message):
|
||||
"""Initialize the exception."""
|
||||
super().__init__()
|
||||
self.message = message
|
||||
CONNECT_TIMEOUT = 30
|
||||
CONNECT_INTERVAL = 1
|
||||
|
||||
|
||||
class DynaliteBridge:
|
||||
"""Manages a single Dynalite bridge."""
|
||||
|
||||
def __init__(self, hass, config_entry):
|
||||
def __init__(self, hass, config):
|
||||
"""Initialize the system based on host parameter."""
|
||||
self.config_entry = config_entry
|
||||
self.hass = hass
|
||||
self.area = {}
|
||||
self.async_add_entities = None
|
||||
self.waiting_entities = []
|
||||
self.all_entities = {}
|
||||
self.config = None
|
||||
self.host = config_entry.data[CONF_HOST]
|
||||
if self.host not in hass.data[DOMAIN][DATA_CONFIGS]:
|
||||
LOGGER.info("invalid host - %s", self.host)
|
||||
raise BridgeError(f"invalid host - {self.host}")
|
||||
self.config = hass.data[DOMAIN][DATA_CONFIGS][self.host]
|
||||
self.async_add_devices = None
|
||||
self.waiting_devices = []
|
||||
self.host = config[CONF_HOST]
|
||||
# Configure the dynalite devices
|
||||
self.dynalite_devices = DynaliteDevices(
|
||||
config=self.config,
|
||||
newDeviceFunc=self.add_devices,
|
||||
config=config,
|
||||
newDeviceFunc=self.add_devices_when_registered,
|
||||
updateDeviceFunc=self.update_device,
|
||||
)
|
||||
|
||||
async def async_setup(self, tries=0):
|
||||
async def async_setup(self):
|
||||
"""Set up a Dynalite bridge."""
|
||||
# Configure the dynalite devices
|
||||
await self.dynalite_devices.async_setup()
|
||||
return await self.dynalite_devices.async_setup()
|
||||
|
||||
self.hass.async_create_task(
|
||||
self.hass.config_entries.async_forward_entry_setup(
|
||||
self.config_entry, "light"
|
||||
)
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
@callback
|
||||
def add_devices(self, devices):
|
||||
"""Call when devices should be added to home assistant."""
|
||||
added_entities = []
|
||||
|
||||
for device in devices:
|
||||
if device.category == "light":
|
||||
entity = DynaliteLight(device, self)
|
||||
def update_signal(self, device=None):
|
||||
"""Create signal to use to trigger entity update."""
|
||||
if device:
|
||||
signal = f"dynalite-update-{self.host}-{device.unique_id}"
|
||||
else:
|
||||
LOGGER.debug("Illegal device category %s", device.category)
|
||||
continue
|
||||
added_entities.append(entity)
|
||||
self.all_entities[entity.unique_id] = entity
|
||||
|
||||
if added_entities:
|
||||
self.add_entities_when_registered(added_entities)
|
||||
signal = f"dynalite-update-{self.host}"
|
||||
return signal
|
||||
|
||||
@callback
|
||||
def update_device(self, device):
|
||||
"""Call when a device or all devices should be updated."""
|
||||
if device == CONF_ALL:
|
||||
# This is used to signal connection or disconnection, so all devices may become available or not.
|
||||
log_string = (
|
||||
"Connected" if self.dynalite_devices.available else "Disconnected"
|
||||
)
|
||||
LOGGER.info("%s to dynalite host", log_string)
|
||||
async_dispatcher_send(self.hass, self.update_signal())
|
||||
else:
|
||||
async_dispatcher_send(self.hass, self.update_signal(device))
|
||||
|
||||
async def try_connection(self):
|
||||
"""Try to connect to dynalite with timeout."""
|
||||
# Currently by polling. Future - will need to change the library to be proactive
|
||||
for _ in range(0, CONNECT_TIMEOUT):
|
||||
if self.dynalite_devices.available:
|
||||
LOGGER.info("Connected to dynalite host")
|
||||
else:
|
||||
LOGGER.info("Disconnected from dynalite host")
|
||||
for uid in self.all_entities:
|
||||
self.all_entities[uid].try_schedule_ha()
|
||||
else:
|
||||
uid = device.unique_id
|
||||
if uid in self.all_entities:
|
||||
self.all_entities[uid].try_schedule_ha()
|
||||
return True
|
||||
await asyncio.sleep(CONNECT_INTERVAL)
|
||||
return False
|
||||
|
||||
@callback
|
||||
def register_add_entities(self, async_add_entities):
|
||||
def register_add_devices(self, async_add_devices):
|
||||
"""Add an async_add_entities for a category."""
|
||||
self.async_add_entities = async_add_entities
|
||||
if self.waiting_entities:
|
||||
self.async_add_entities(self.waiting_entities)
|
||||
self.async_add_devices = async_add_devices
|
||||
if self.waiting_devices:
|
||||
self.async_add_devices(self.waiting_devices)
|
||||
|
||||
def add_entities_when_registered(self, entities):
|
||||
"""Add the entities to HA if async_add_entities was registered, otherwise queue until it is."""
|
||||
if not entities:
|
||||
def add_devices_when_registered(self, devices):
|
||||
"""Add the devices to HA if the add devices callback was registered, otherwise queue until it is."""
|
||||
if not devices:
|
||||
return
|
||||
if self.async_add_entities:
|
||||
self.async_add_entities(entities)
|
||||
if self.async_add_devices:
|
||||
self.async_add_devices(devices)
|
||||
else: # handle it later when it is registered
|
||||
self.waiting_entities.extend(entities)
|
||||
|
||||
async def async_reset(self):
|
||||
"""Reset this bridge to default state.
|
||||
|
||||
Will cancel any scheduled setup retry and will unload
|
||||
the config entry.
|
||||
"""
|
||||
result = await self.hass.config_entries.async_forward_entry_unload(
|
||||
self.config_entry, "light"
|
||||
)
|
||||
# None and True are OK
|
||||
return result
|
||||
self.waiting_devices.extend(devices)
|
||||
|
|
|
@ -1,19 +1,9 @@
|
|||
"""Config flow to configure Dynalite hub."""
|
||||
import asyncio
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_HOST
|
||||
from homeassistant.core import callback
|
||||
|
||||
from .const import DOMAIN, LOGGER
|
||||
|
||||
|
||||
@callback
|
||||
def configured_hosts(hass):
|
||||
"""Return a set of the configured hosts."""
|
||||
return set(
|
||||
entry.data[CONF_HOST] for entry in hass.config_entries.async_entries(DOMAIN)
|
||||
)
|
||||
from .bridge import DynaliteBridge
|
||||
from .const import DOMAIN, LOGGER # pylint: disable=unused-import
|
||||
|
||||
|
||||
class DynaliteFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
@ -30,29 +20,16 @@ class DynaliteFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
|
||||
async def async_step_import(self, import_info):
|
||||
"""Import a new bridge as a config entry."""
|
||||
LOGGER.debug("async_step_import - %s", import_info)
|
||||
host = self.context[CONF_HOST] = import_info[CONF_HOST]
|
||||
return await self._entry_from_bridge(host)
|
||||
|
||||
async def _entry_from_bridge(self, host):
|
||||
"""Return a config entry from an initialized bridge."""
|
||||
LOGGER.debug("entry_from_bridge - %s", host)
|
||||
# Remove all other entries of hubs with same ID or host
|
||||
|
||||
same_hub_entries = [
|
||||
entry.entry_id
|
||||
for entry in self.hass.config_entries.async_entries(DOMAIN)
|
||||
if entry.data[CONF_HOST] == host
|
||||
]
|
||||
|
||||
LOGGER.debug("entry_from_bridge same_hub - %s", same_hub_entries)
|
||||
|
||||
if same_hub_entries:
|
||||
await asyncio.wait(
|
||||
[
|
||||
self.hass.config_entries.async_remove(entry_id)
|
||||
for entry_id in same_hub_entries
|
||||
]
|
||||
)
|
||||
|
||||
return self.async_create_entry(title=host, data={CONF_HOST: host})
|
||||
LOGGER.debug("Starting async_step_import - %s", import_info)
|
||||
host = import_info[CONF_HOST]
|
||||
await self.async_set_unique_id(host)
|
||||
self._abort_if_unique_id_configured(import_info)
|
||||
# New entry
|
||||
bridge = DynaliteBridge(self.hass, import_info)
|
||||
if not await bridge.async_setup():
|
||||
LOGGER.error("Unable to setup bridge - import info=%s", import_info)
|
||||
return self.async_abort(reason="bridge_setup_failed")
|
||||
if not await bridge.try_connection():
|
||||
return self.async_abort(reason="no_connection")
|
||||
LOGGER.debug("Creating entry for the bridge - %s", import_info)
|
||||
return self.async_create_entry(title=host, data=import_info)
|
||||
|
|
|
@ -3,9 +3,19 @@ import logging
|
|||
|
||||
LOGGER = logging.getLogger(__package__)
|
||||
DOMAIN = "dynalite"
|
||||
DATA_CONFIGS = "dynalite_configs"
|
||||
|
||||
CONF_ACTIVE = "active"
|
||||
CONF_ALL = "ALL"
|
||||
CONF_AREA = "area"
|
||||
CONF_AUTO_DISCOVER = "autodiscover"
|
||||
CONF_BRIDGES = "bridges"
|
||||
CONF_CHANNEL = "channel"
|
||||
CONF_DEFAULT = "default"
|
||||
CONF_FADE = "fade"
|
||||
CONF_HOST = "host"
|
||||
CONF_NAME = "name"
|
||||
CONF_POLLTIMER = "polltimer"
|
||||
CONF_PORT = "port"
|
||||
|
||||
DEFAULT_NAME = "dynalite"
|
||||
DEFAULT_PORT = 12345
|
||||
|
|
|
@ -1,15 +1,26 @@
|
|||
"""Support for Dynalite channels as lights."""
|
||||
from homeassistant.components.light import SUPPORT_BRIGHTNESS, Light
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
|
||||
from .const import DOMAIN, LOGGER
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Record the async_add_entities function to add them later when received from Dynalite."""
|
||||
LOGGER.debug("async_setup_entry light entry = %s", config_entry.data)
|
||||
LOGGER.debug("Setting up light entry = %s", config_entry.data)
|
||||
bridge = hass.data[DOMAIN][config_entry.entry_id]
|
||||
bridge.register_add_entities(async_add_entities)
|
||||
|
||||
@callback
|
||||
def async_add_lights(devices):
|
||||
added_lights = []
|
||||
for device in devices:
|
||||
if device.category == "light":
|
||||
added_lights.append(DynaliteLight(device, bridge))
|
||||
if added_lights:
|
||||
async_add_entities(added_lights)
|
||||
|
||||
bridge.register_add_devices(async_add_lights)
|
||||
|
||||
|
||||
class DynaliteLight(Light):
|
||||
|
@ -20,11 +31,6 @@ class DynaliteLight(Light):
|
|||
self._device = device
|
||||
self._bridge = bridge
|
||||
|
||||
@property
|
||||
def device(self):
|
||||
"""Return the underlying device - mostly for testing."""
|
||||
return self._device
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the entity."""
|
||||
|
@ -40,11 +46,6 @@ class DynaliteLight(Light):
|
|||
"""Return if entity is available."""
|
||||
return self._device.available
|
||||
|
||||
@property
|
||||
def hidden(self):
|
||||
"""Return true if this entity should be hidden from UI."""
|
||||
return self._device.hidden
|
||||
|
||||
async def async_update(self):
|
||||
"""Update the entity."""
|
||||
return
|
||||
|
@ -52,7 +53,11 @@ class DynaliteLight(Light):
|
|||
@property
|
||||
def device_info(self):
|
||||
"""Device info for this entity."""
|
||||
return self._device.device_info
|
||||
return {
|
||||
"identifiers": {(DOMAIN, self.unique_id)},
|
||||
"name": self.name,
|
||||
"manufacturer": "Dynalite",
|
||||
}
|
||||
|
||||
@property
|
||||
def brightness(self):
|
||||
|
@ -77,8 +82,15 @@ class DynaliteLight(Light):
|
|||
"""Flag supported features."""
|
||||
return SUPPORT_BRIGHTNESS
|
||||
|
||||
@callback
|
||||
def try_schedule_ha(self):
|
||||
"""Schedule update HA state if configured."""
|
||||
if self.hass:
|
||||
self.schedule_update_ha_state()
|
||||
async def async_added_to_hass(self):
|
||||
"""Added to hass so need to register to dispatch."""
|
||||
# register for device specific update
|
||||
async_dispatcher_connect(
|
||||
self.hass,
|
||||
self._bridge.update_signal(self._device),
|
||||
self.async_schedule_update_ha_state,
|
||||
)
|
||||
# register for wide update
|
||||
async_dispatcher_connect(
|
||||
self.hass, self._bridge.update_signal(), self.async_schedule_update_ha_state
|
||||
)
|
||||
|
|
|
@ -5,5 +5,5 @@
|
|||
"documentation": "https://www.home-assistant.io/integrations/dynalite",
|
||||
"dependencies": [],
|
||||
"codeowners": ["@ziv1234"],
|
||||
"requirements": ["dynalite_devices==0.1.17"]
|
||||
"requirements": ["dynalite_devices==0.1.22"]
|
||||
}
|
||||
|
|
|
@ -456,7 +456,7 @@ dsmr_parser==0.18
|
|||
dweepy==0.3.0
|
||||
|
||||
# homeassistant.components.dynalite
|
||||
dynalite_devices==0.1.17
|
||||
dynalite_devices==0.1.22
|
||||
|
||||
# homeassistant.components.rainforest_eagle
|
||||
eagle200_reader==0.2.1
|
||||
|
|
|
@ -168,7 +168,7 @@ distro==1.4.0
|
|||
dsmr_parser==0.18
|
||||
|
||||
# homeassistant.components.dynalite
|
||||
dynalite_devices==0.1.17
|
||||
dynalite_devices==0.1.22
|
||||
|
||||
# homeassistant.components.ee_brightbox
|
||||
eebrightbox==0.0.4
|
||||
|
|
|
@ -1,136 +1,81 @@
|
|||
"""Test Dynalite bridge."""
|
||||
from unittest.mock import Mock, call, patch
|
||||
from unittest.mock import Mock, call
|
||||
|
||||
from asynctest import patch
|
||||
from dynalite_lib import CONF_ALL
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.dynalite import DATA_CONFIGS, DOMAIN
|
||||
from homeassistant.components.dynalite.bridge import BridgeError, DynaliteBridge
|
||||
|
||||
from tests.common import mock_coro
|
||||
from homeassistant.components import dynalite
|
||||
|
||||
|
||||
async def test_bridge_setup():
|
||||
@pytest.fixture
|
||||
def dyn_bridge():
|
||||
"""Define a basic mock bridge."""
|
||||
hass = Mock()
|
||||
host = "1.2.3.4"
|
||||
bridge = dynalite.DynaliteBridge(hass, {dynalite.CONF_HOST: host})
|
||||
return bridge
|
||||
|
||||
|
||||
async def test_update_device(dyn_bridge):
|
||||
"""Test a successful setup."""
|
||||
hass = Mock()
|
||||
entry = Mock()
|
||||
host = "1.2.3.4"
|
||||
entry.data = {"host": host}
|
||||
hass.data = {DOMAIN: {DATA_CONFIGS: {host: {}}}}
|
||||
dyn_bridge = DynaliteBridge(hass, entry)
|
||||
async_dispatch = Mock()
|
||||
|
||||
with patch.object(
|
||||
dyn_bridge.dynalite_devices, "async_setup", return_value=mock_coro(True)
|
||||
with patch(
|
||||
"homeassistant.components.dynalite.bridge.async_dispatcher_send", async_dispatch
|
||||
):
|
||||
assert await dyn_bridge.async_setup() is True
|
||||
|
||||
forward_entries = set(
|
||||
c[1][1] for c in hass.config_entries.async_forward_entry_setup.mock_calls
|
||||
dyn_bridge.update_device(CONF_ALL)
|
||||
async_dispatch.assert_called_once()
|
||||
assert async_dispatch.mock_calls[0] == call(
|
||||
dyn_bridge.hass, f"dynalite-update-{dyn_bridge.host}"
|
||||
)
|
||||
async_dispatch.reset_mock()
|
||||
device = Mock
|
||||
device.unique_id = "abcdef"
|
||||
dyn_bridge.update_device(device)
|
||||
async_dispatch.assert_called_once()
|
||||
assert async_dispatch.mock_calls[0] == call(
|
||||
dyn_bridge.hass, f"dynalite-update-{dyn_bridge.host}-{device.unique_id}"
|
||||
)
|
||||
hass.config_entries.async_forward_entry_setup.assert_called_once()
|
||||
assert forward_entries == set(["light"])
|
||||
|
||||
|
||||
async def test_invalid_host():
|
||||
"""Test without host in hass.data."""
|
||||
hass = Mock()
|
||||
entry = Mock()
|
||||
host = "1.2.3.4"
|
||||
entry.data = {"host": host}
|
||||
hass.data = {DOMAIN: {DATA_CONFIGS: {}}}
|
||||
|
||||
dyn_bridge = None
|
||||
with pytest.raises(BridgeError):
|
||||
dyn_bridge = DynaliteBridge(hass, entry)
|
||||
assert dyn_bridge is None
|
||||
|
||||
|
||||
async def test_add_devices_then_register():
|
||||
async def test_add_devices_then_register(dyn_bridge):
|
||||
"""Test that add_devices work."""
|
||||
hass = Mock()
|
||||
entry = Mock()
|
||||
host = "1.2.3.4"
|
||||
entry.data = {"host": host}
|
||||
hass.data = {DOMAIN: {DATA_CONFIGS: {host: {}}}}
|
||||
dyn_bridge = DynaliteBridge(hass, entry)
|
||||
|
||||
# First test empty
|
||||
dyn_bridge.add_devices_when_registered([])
|
||||
assert not dyn_bridge.waiting_devices
|
||||
# Now with devices
|
||||
device1 = Mock()
|
||||
device1.category = "light"
|
||||
device2 = Mock()
|
||||
device2.category = "switch"
|
||||
dyn_bridge.add_devices([device1, device2])
|
||||
dyn_bridge.add_devices_when_registered([device1, device2])
|
||||
reg_func = Mock()
|
||||
dyn_bridge.register_add_entities(reg_func)
|
||||
dyn_bridge.register_add_devices(reg_func)
|
||||
reg_func.assert_called_once()
|
||||
assert reg_func.mock_calls[0][1][0][0].device is device1
|
||||
assert reg_func.mock_calls[0][1][0][0] is device1
|
||||
|
||||
|
||||
async def test_register_then_add_devices():
|
||||
async def test_register_then_add_devices(dyn_bridge):
|
||||
"""Test that add_devices work after register_add_entities."""
|
||||
hass = Mock()
|
||||
entry = Mock()
|
||||
host = "1.2.3.4"
|
||||
entry.data = {"host": host}
|
||||
hass.data = {DOMAIN: {DATA_CONFIGS: {host: {}}}}
|
||||
dyn_bridge = DynaliteBridge(hass, entry)
|
||||
|
||||
device1 = Mock()
|
||||
device1.category = "light"
|
||||
device2 = Mock()
|
||||
device2.category = "switch"
|
||||
reg_func = Mock()
|
||||
dyn_bridge.register_add_entities(reg_func)
|
||||
dyn_bridge.add_devices([device1, device2])
|
||||
dyn_bridge.register_add_devices(reg_func)
|
||||
dyn_bridge.add_devices_when_registered([device1, device2])
|
||||
reg_func.assert_called_once()
|
||||
assert reg_func.mock_calls[0][1][0][0].device is device1
|
||||
assert reg_func.mock_calls[0][1][0][0] is device1
|
||||
|
||||
|
||||
async def test_update_device():
|
||||
"""Test the update_device callback."""
|
||||
hass = Mock()
|
||||
entry = Mock()
|
||||
host = "1.2.3.4"
|
||||
entry.data = {"host": host}
|
||||
hass.data = {DOMAIN: {DATA_CONFIGS: {host: {}}}}
|
||||
dyn_bridge = DynaliteBridge(hass, entry)
|
||||
with patch.object(dyn_bridge, "dynalite_devices") as devices_mock:
|
||||
# Single device update
|
||||
device1 = Mock()
|
||||
device1.unique_id = "testing1"
|
||||
device2 = Mock()
|
||||
device2.unique_id = "testing2"
|
||||
dyn_bridge.all_entities = {
|
||||
device1.unique_id: device1,
|
||||
device2.unique_id: device2,
|
||||
}
|
||||
dyn_bridge.update_device(device1)
|
||||
device1.try_schedule_ha.assert_called_once()
|
||||
device2.try_schedule_ha.assert_not_called()
|
||||
# connected to network - all devices update
|
||||
devices_mock.available = True
|
||||
dyn_bridge.update_device(CONF_ALL)
|
||||
assert device1.try_schedule_ha.call_count == 2
|
||||
device2.try_schedule_ha.assert_called_once()
|
||||
# disconnected from network - all devices update
|
||||
devices_mock.available = False
|
||||
dyn_bridge.update_device(CONF_ALL)
|
||||
assert device1.try_schedule_ha.call_count == 3
|
||||
assert device2.try_schedule_ha.call_count == 2
|
||||
|
||||
|
||||
async def test_async_reset():
|
||||
"""Test async_reset."""
|
||||
hass = Mock()
|
||||
hass.config_entries.async_forward_entry_unload = Mock(
|
||||
return_value=mock_coro(Mock())
|
||||
)
|
||||
entry = Mock()
|
||||
host = "1.2.3.4"
|
||||
entry.data = {"host": host}
|
||||
hass.data = {DOMAIN: {DATA_CONFIGS: {host: {}}}}
|
||||
dyn_bridge = DynaliteBridge(hass, entry)
|
||||
await dyn_bridge.async_reset()
|
||||
hass.config_entries.async_forward_entry_unload.assert_called_once()
|
||||
assert hass.config_entries.async_forward_entry_unload.mock_calls[0] == call(
|
||||
entry, "light"
|
||||
)
|
||||
async def test_try_connection(dyn_bridge):
|
||||
"""Test that try connection works."""
|
||||
# successful
|
||||
with patch.object(dyn_bridge.dynalite_devices, "connected", True):
|
||||
assert await dyn_bridge.try_connection()
|
||||
# unsuccessful
|
||||
with patch.object(dyn_bridge.dynalite_devices, "connected", False), patch(
|
||||
"homeassistant.components.dynalite.bridge.CONNECT_INTERVAL", 0
|
||||
):
|
||||
assert not await dyn_bridge.try_connection()
|
||||
|
|
|
@ -1,36 +1,90 @@
|
|||
"""Test Dynalite config flow."""
|
||||
from unittest.mock import Mock, call, patch
|
||||
from asynctest import patch
|
||||
|
||||
from homeassistant.components.dynalite import config_flow
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components import dynalite
|
||||
|
||||
from tests.common import mock_coro
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_step_import():
|
||||
"""Test a successful setup."""
|
||||
flow_handler = config_flow.DynaliteFlowHandler()
|
||||
with patch.object(flow_handler, "context", create=True):
|
||||
with patch.object(flow_handler, "hass", create=True) as mock_hass:
|
||||
with patch.object(
|
||||
flow_handler, "async_create_entry", create=True
|
||||
) as mock_create:
|
||||
async def run_flow(hass, setup, connection):
|
||||
"""Run a flow with or without errors and return result."""
|
||||
host = "1.2.3.4"
|
||||
entry1 = Mock()
|
||||
entry1.data = {"host": host}
|
||||
entry2 = Mock()
|
||||
entry2.data = {"host": "5.5"}
|
||||
mock_hass.config_entries.async_entries = Mock(
|
||||
return_value=[entry1, entry2]
|
||||
with patch(
|
||||
"homeassistant.components.dynalite.bridge.DynaliteDevices.async_setup",
|
||||
return_value=setup,
|
||||
), patch(
|
||||
"homeassistant.components.dynalite.bridge.DynaliteDevices.available", connection
|
||||
), patch(
|
||||
"homeassistant.components.dynalite.bridge.CONNECT_INTERVAL", 0
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
dynalite.DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={dynalite.CONF_HOST: host},
|
||||
)
|
||||
mock_hass.config_entries.async_remove = Mock(
|
||||
return_value=mock_coro(Mock())
|
||||
return result
|
||||
|
||||
|
||||
async def test_flow_works(hass):
|
||||
"""Test a successful config flow."""
|
||||
result = await run_flow(hass, True, True)
|
||||
assert result["type"] == "create_entry"
|
||||
|
||||
|
||||
async def test_flow_setup_fails(hass):
|
||||
"""Test a flow where async_setup fails."""
|
||||
result = await run_flow(hass, False, True)
|
||||
assert result["type"] == "abort"
|
||||
assert result["reason"] == "bridge_setup_failed"
|
||||
|
||||
|
||||
async def test_flow_no_connection(hass):
|
||||
"""Test a flow where connection times out."""
|
||||
result = await run_flow(hass, True, False)
|
||||
assert result["type"] == "abort"
|
||||
assert result["reason"] == "no_connection"
|
||||
|
||||
|
||||
async def test_existing(hass):
|
||||
"""Test when the entry exists with the same config."""
|
||||
host = "1.2.3.4"
|
||||
MockConfigEntry(
|
||||
domain=dynalite.DOMAIN, unique_id=host, data={dynalite.CONF_HOST: host}
|
||||
).add_to_hass(hass)
|
||||
with patch(
|
||||
"homeassistant.components.dynalite.bridge.DynaliteDevices.async_setup",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.dynalite.bridge.DynaliteDevices.available", True
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
dynalite.DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={dynalite.CONF_HOST: host},
|
||||
)
|
||||
await flow_handler.async_step_import({"host": "1.2.3.4"})
|
||||
mock_hass.config_entries.async_remove.assert_called_once()
|
||||
assert mock_hass.config_entries.async_remove.mock_calls[0] == call(
|
||||
entry1.entry_id
|
||||
assert result["type"] == "abort"
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_existing_update(hass):
|
||||
"""Test when the entry exists with the same config."""
|
||||
host = "1.2.3.4"
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=dynalite.DOMAIN, unique_id=host, data={dynalite.CONF_HOST: host}
|
||||
)
|
||||
mock_create.assert_called_once()
|
||||
assert mock_create.mock_calls[0] == call(
|
||||
title=host, data={"host": host}
|
||||
mock_entry.add_to_hass(hass)
|
||||
with patch(
|
||||
"homeassistant.components.dynalite.bridge.DynaliteDevices.async_setup",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.dynalite.bridge.DynaliteDevices.available", True
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
dynalite.DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={dynalite.CONF_HOST: host, "aaa": "bbb"},
|
||||
)
|
||||
assert result["type"] == "abort"
|
||||
assert result["reason"] == "already_configured"
|
||||
assert mock_entry.data.get("aaa") == "bbb"
|
||||
|
|
|
@ -1,74 +1,62 @@
|
|||
"""Test Dynalite __init__."""
|
||||
from unittest.mock import Mock, call, patch
|
||||
|
||||
from homeassistant.components.dynalite import DATA_CONFIGS, DOMAIN, LOGGER
|
||||
from homeassistant.components.dynalite.__init__ import (
|
||||
async_setup,
|
||||
async_setup_entry,
|
||||
async_unload_entry,
|
||||
)
|
||||
from asynctest import patch
|
||||
|
||||
from tests.common import mock_coro
|
||||
from homeassistant.components import dynalite
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_async_setup():
|
||||
async def test_empty_config(hass):
|
||||
"""Test with an empty config."""
|
||||
assert await async_setup_component(hass, dynalite.DOMAIN, {}) is True
|
||||
assert len(hass.config_entries.flow.async_progress()) == 0
|
||||
assert hass.data[dynalite.DOMAIN] == {}
|
||||
|
||||
|
||||
async def test_async_setup(hass):
|
||||
"""Test a successful setup."""
|
||||
new_host = "1.2.3.4"
|
||||
old_host = "5.6.7.8"
|
||||
hass = Mock()
|
||||
hass.data = {}
|
||||
config = {DOMAIN: {"bridges": [{"host": old_host}, {"host": new_host}]}}
|
||||
mock_conf_host = Mock(return_value=[old_host])
|
||||
with patch(
|
||||
"homeassistant.components.dynalite.__init__.configured_hosts", mock_conf_host
|
||||
):
|
||||
await async_setup(hass, config)
|
||||
mock_conf_host.assert_called_once()
|
||||
assert mock_conf_host.mock_calls[0] == call(hass)
|
||||
assert hass.data[DOMAIN][DATA_CONFIGS] == {
|
||||
new_host: {"host": new_host},
|
||||
old_host: {"host": old_host},
|
||||
}
|
||||
hass.async_create_task.assert_called_once()
|
||||
|
||||
|
||||
async def test_async_setup_entry():
|
||||
"""Test setup of an entry."""
|
||||
|
||||
def async_mock(mock):
|
||||
"""Return the return value of a mock from async."""
|
||||
|
||||
async def async_func(*args, **kwargs):
|
||||
return mock()
|
||||
|
||||
return async_func
|
||||
|
||||
host = "1.2.3.4"
|
||||
hass = Mock()
|
||||
entry = Mock()
|
||||
entry.data = {"host": host}
|
||||
hass.data = {}
|
||||
hass.data[DOMAIN] = {}
|
||||
hass.data[DOMAIN][DATA_CONFIGS] = {host: {}}
|
||||
mock_async_setup = Mock(return_value=True)
|
||||
with patch(
|
||||
"homeassistant.components.dynalite.__init__.DynaliteBridge.async_setup",
|
||||
async_mock(mock_async_setup),
|
||||
):
|
||||
assert await async_setup_entry(hass, entry)
|
||||
mock_async_setup.assert_called_once()
|
||||
"dynalite_devices_lib.DynaliteDevices.async_setup", return_value=True
|
||||
), patch("dynalite_devices_lib.DynaliteDevices.available", True):
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
dynalite.DOMAIN,
|
||||
{dynalite.DOMAIN: {dynalite.CONF_BRIDGES: [{dynalite.CONF_HOST: host}]}},
|
||||
)
|
||||
|
||||
assert len(hass.data[dynalite.DOMAIN]) == 1
|
||||
|
||||
|
||||
async def test_async_unload_entry():
|
||||
"""Test unloading of an entry."""
|
||||
hass = Mock()
|
||||
mock_bridge = Mock()
|
||||
mock_bridge.async_reset.return_value = mock_coro(True)
|
||||
entry = Mock()
|
||||
hass.data = {}
|
||||
hass.data[DOMAIN] = {}
|
||||
hass.data[DOMAIN][entry.entry_id] = mock_bridge
|
||||
await async_unload_entry(hass, entry)
|
||||
LOGGER.error("XXX calls=%s", mock_bridge.mock_calls)
|
||||
mock_bridge.async_reset.assert_called_once()
|
||||
assert mock_bridge.mock_calls[0] == call.async_reset()
|
||||
async def test_async_setup_failed(hass):
|
||||
"""Test a setup when DynaliteBridge.async_setup fails."""
|
||||
host = "1.2.3.4"
|
||||
with patch("dynalite_devices_lib.DynaliteDevices.async_setup", return_value=False):
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
dynalite.DOMAIN,
|
||||
{dynalite.DOMAIN: {dynalite.CONF_BRIDGES: [{dynalite.CONF_HOST: host}]}},
|
||||
)
|
||||
assert hass.data[dynalite.DOMAIN] == {}
|
||||
|
||||
|
||||
async def test_unload_entry(hass):
|
||||
"""Test being able to unload an entry."""
|
||||
host = "1.2.3.4"
|
||||
entry = MockConfigEntry(domain=dynalite.DOMAIN, data={"host": host})
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"dynalite_devices_lib.DynaliteDevices.async_setup", return_value=True
|
||||
), patch("dynalite_devices_lib.DynaliteDevices.available", True):
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
dynalite.DOMAIN,
|
||||
{dynalite.DOMAIN: {dynalite.CONF_BRIDGES: [{dynalite.CONF_HOST: host}]}},
|
||||
)
|
||||
assert hass.data[dynalite.DOMAIN].get(entry.entry_id)
|
||||
|
||||
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||
assert not hass.data[dynalite.DOMAIN].get(entry.entry_id)
|
||||
|
|
|
@ -1,44 +1,78 @@
|
|||
"""Test Dynalite light."""
|
||||
from unittest.mock import Mock, call, patch
|
||||
from unittest.mock import Mock
|
||||
|
||||
from homeassistant.components.dynalite import DOMAIN
|
||||
from homeassistant.components.dynalite.light import DynaliteLight, async_setup_entry
|
||||
from asynctest import CoroutineMock, patch
|
||||
import pytest
|
||||
|
||||
from tests.common import mock_coro
|
||||
from homeassistant.components import dynalite
|
||||
from homeassistant.components.light import SUPPORT_BRIGHTNESS
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
|
||||
async def test_light_setup():
|
||||
"""Test a successful setup."""
|
||||
hass = Mock()
|
||||
entry = Mock()
|
||||
async_add = Mock()
|
||||
bridge = Mock()
|
||||
hass.data = {DOMAIN: {entry.entry_id: bridge}}
|
||||
await async_setup_entry(hass, entry, async_add)
|
||||
bridge.register_add_entities.assert_called_once()
|
||||
assert bridge.register_add_entities.mock_calls[0] == call(async_add)
|
||||
|
||||
|
||||
async def test_light():
|
||||
"""Test the light entity."""
|
||||
@pytest.fixture
|
||||
def mock_device():
|
||||
"""Mock a Dynalite device."""
|
||||
device = Mock()
|
||||
device.async_turn_on = Mock(return_value=mock_coro(Mock()))
|
||||
device.async_turn_off = Mock(return_value=mock_coro(Mock()))
|
||||
bridge = Mock()
|
||||
dyn_light = DynaliteLight(device, bridge)
|
||||
assert dyn_light.name is device.name
|
||||
assert dyn_light.unique_id is device.unique_id
|
||||
assert dyn_light.available is device.available
|
||||
assert dyn_light.hidden is device.hidden
|
||||
await dyn_light.async_update() # does nothing
|
||||
assert dyn_light.device_info is device.device_info
|
||||
assert dyn_light.brightness is device.brightness
|
||||
assert dyn_light.is_on is device.is_on
|
||||
await dyn_light.async_turn_on(aaa="bbb")
|
||||
assert device.async_turn_on.mock_calls[0] == call(aaa="bbb")
|
||||
await dyn_light.async_turn_off(ccc="ddd")
|
||||
assert device.async_turn_off.mock_calls[0] == call(ccc="ddd")
|
||||
with patch.object(dyn_light, "hass"):
|
||||
with patch.object(dyn_light, "schedule_update_ha_state") as update_ha:
|
||||
dyn_light.try_schedule_ha()
|
||||
update_ha.assert_called_once()
|
||||
device.category = "light"
|
||||
device.unique_id = "UNIQUE"
|
||||
device.name = "NAME"
|
||||
device.device_info = {
|
||||
"identifiers": {(dynalite.DOMAIN, device.unique_id)},
|
||||
"name": device.name,
|
||||
"manufacturer": "Dynalite",
|
||||
}
|
||||
return device
|
||||
|
||||
|
||||
async def create_light_from_device(hass, device):
|
||||
"""Set up the component and platform and create a light based on the device provided."""
|
||||
host = "1.2.3.4"
|
||||
with patch(
|
||||
"homeassistant.components.dynalite.bridge.DynaliteDevices.async_setup",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.dynalite.bridge.DynaliteDevices.available", True
|
||||
):
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
dynalite.DOMAIN,
|
||||
{dynalite.DOMAIN: {dynalite.CONF_BRIDGES: [{dynalite.CONF_HOST: host}]}},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
# Find the bridge
|
||||
bridge = None
|
||||
assert len(hass.data[dynalite.DOMAIN]) == 1
|
||||
key = next(iter(hass.data[dynalite.DOMAIN]))
|
||||
bridge = hass.data[dynalite.DOMAIN][key]
|
||||
bridge.dynalite_devices.newDeviceFunc([device])
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def test_light_setup(hass, mock_device):
|
||||
"""Test a successful setup."""
|
||||
await create_light_from_device(hass, mock_device)
|
||||
entity_state = hass.states.get("light.name")
|
||||
assert entity_state.attributes["brightness"] == mock_device.brightness
|
||||
assert entity_state.attributes["supported_features"] == SUPPORT_BRIGHTNESS
|
||||
|
||||
|
||||
async def test_turn_on(hass, mock_device):
|
||||
"""Test turning a light on."""
|
||||
mock_device.async_turn_on = CoroutineMock(return_value=True)
|
||||
await create_light_from_device(hass, mock_device)
|
||||
await hass.services.async_call(
|
||||
"light", "turn_on", {"entity_id": "light.name"}, blocking=True
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
mock_device.async_turn_on.assert_awaited_once()
|
||||
|
||||
|
||||
async def test_turn_off(hass, mock_device):
|
||||
"""Test turning a light off."""
|
||||
mock_device.async_turn_off = CoroutineMock(return_value=True)
|
||||
await create_light_from_device(hass, mock_device)
|
||||
await hass.services.async_call(
|
||||
"light", "turn_off", {"entity_id": "light.name"}, blocking=True
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
mock_device.async_turn_off.assert_awaited_once()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue