Compare commits

...

16 Commits

Author SHA1 Message Date
Paulus Schoutsen
111a00aeeb Bumped version to 0.111.0b4 2020-06-08 12:28:53 -07:00
Franck Nijhof
b3d5717df8 Fix mobile_app sensor re-registration handling (#36567) 2020-06-08 12:27:12 -07:00
Paulus Schoutsen
a84378bb79 Mobile app fixes (#36559) 2020-06-08 12:27:11 -07:00
shbatm
53ba45cc8f Add Z-Wave Notification Sensor support to ISY994 (#36548) 2020-06-08 12:27:10 -07:00
Jörg Thalheim
5d77eb1839 Fix intent component initialisation (#36064)
The intent component expect this method from every module that is called intent.
Fixes #35522
2020-06-08 12:27:09 -07:00
Paulus Schoutsen
517159dc4e Merge remote-tracking branch 'origin/master' into rc 2020-06-08 11:17:27 -07:00
Paulus Schoutsen
a9287b7117 Merge pull request #36579 from home-assistant/110.6 2020-06-08 11:15:35 -07:00
Paulus Schoutsen
0b7bcc87df Bumped version to 0.110.6 2020-06-08 10:14:45 -07:00
Franck Nijhof
482661f82c Fix mobile_app registering/update sensor values with an unknown state (#36566) 2020-06-08 10:14:40 -07:00
Paulus Schoutsen
865d65c380 Bumped version to 0.111.0b3 2020-06-07 17:24:48 -07:00
Franck Nijhof
609d202c4d Fix WLED power and brightness with WLED 0.10+ (#36529) 2020-06-07 17:24:43 -07:00
matgad
e43a0087e4 Bump version zigpy-cc (#36506) 2020-06-07 17:24:43 -07:00
Paulus Schoutsen
b9bc147339 Update netdisco (#36499) 2020-06-07 17:24:42 -07:00
Bram Kragten
dd3b0df22d Update frontend to 20200603.2 (#36494) 2020-06-07 17:24:41 -07:00
Erik Montnemery
c987ca735e Bump pychromecast to 6.0.0 (#36414)
* Revert "Prevent race in pychromecast.start_discovery (#36350)"

This reverts commit 391983a0cf.

* Adapt to pychromecast 6.0.0
2020-06-07 17:24:40 -07:00
Paulus Schoutsen
4b1761ccb5 Use builtin mock (#36473) 2020-06-05 15:12:50 -07:00
25 changed files with 537 additions and 169 deletions

View File

@@ -40,6 +40,16 @@ def async_setup(hass):
hass.http.register_view(AlexaIntentsView)
async def async_setup_intents(hass):
"""
Do intents setup.
Right now this module does not expose any, but the intent component breaks
without it.
"""
pass # pylint: disable=unnecessary-pass
class UnknownRequest(HomeAssistantError):
"""When an unknown Alexa request is passed in."""

View File

@@ -3,7 +3,6 @@ import logging
import threading
import pychromecast
import zeroconf
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.core import HomeAssistant
@@ -85,14 +84,12 @@ def setup_internal_discovery(hass: HomeAssistant) -> None:
)
_LOGGER.debug("Starting internal pychromecast discovery.")
listener = pychromecast.discovery.CastListener(
internal_add_callback, internal_remove_callback
)
browser = zeroconf.ServiceBrowser(
ChromeCastZeroconf.get_zeroconf() or zeroconf.Zeroconf(),
"_googlecast._tcp.local.",
listener,
listener = pychromecast.CastListener(
internal_add_callback,
internal_remove_callback,
internal_add_callback, # Use internal_add_callback also for updates
)
browser = pychromecast.start_discovery(listener, ChromeCastZeroconf.get_zeroconf())
def stop_discovery(event):
"""Stop discovery of new chromecasts."""

View File

@@ -3,7 +3,7 @@
"name": "Google Cast",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/cast",
"requirements": ["pychromecast==5.3.0"],
"requirements": ["pychromecast==6.0.0"],
"after_dependencies": ["cloud","zeroconf"],
"zeroconf": ["_googlecast._tcp.local."],
"codeowners": ["@emontnemery"]

View File

@@ -2,7 +2,7 @@
"domain": "discovery",
"name": "Discovery",
"documentation": "https://www.home-assistant.io/integrations/discovery",
"requirements": ["netdisco==2.6.0"],
"requirements": ["netdisco==2.7.0"],
"after_dependencies": ["zeroconf"],
"codeowners": [],
"quality_scale": "internal"

View File

@@ -2,7 +2,7 @@
"domain": "frontend",
"name": "Home Assistant Frontend",
"documentation": "https://www.home-assistant.io/integrations/frontend",
"requirements": ["home-assistant-frontend==20200603.1"],
"requirements": ["home-assistant-frontend==20200603.2"],
"dependencies": [
"api",
"auth",

View File

@@ -232,7 +232,7 @@ NODE_FILTERS = {
"RemoteLinc2_ADV",
],
FILTER_INSTEON_TYPE: ["0.16.", "0.17.", "0.18.", "9.0.", "9.7."],
FILTER_ZWAVE_CAT: (["118", "143"] + list(map(str, range(180, 185)))),
FILTER_ZWAVE_CAT: (["118", "143"] + list(map(str, range(180, 186)))),
},
LOCK: {
FILTER_UOM: ["11"],

View File

@@ -56,7 +56,6 @@ ERR_ENCRYPTION_ALREADY_ENABLED = "encryption_already_enabled"
ERR_ENCRYPTION_NOT_AVAILABLE = "encryption_not_available"
ERR_ENCRYPTION_REQUIRED = "encryption_required"
ERR_SENSOR_NOT_REGISTERED = "not_registered"
ERR_SENSOR_DUPLICATE_UNIQUE_ID = "duplicate_unique_id"
ERR_INVALID_FORMAT = "invalid_format"

View File

@@ -1,4 +1,5 @@
"""Webhook handlers for mobile_app."""
import asyncio
from functools import wraps
import logging
import secrets
@@ -28,7 +29,7 @@ from homeassistant.const import (
HTTP_CREATED,
)
from homeassistant.core import EventOrigin
from homeassistant.exceptions import HomeAssistantError, ServiceNotFound, TemplateError
from homeassistant.exceptions import ServiceNotFound, TemplateError
from homeassistant.helpers import config_validation as cv, device_registry as dr
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.template import attach
@@ -77,7 +78,6 @@ from .const import (
ERR_ENCRYPTION_NOT_AVAILABLE,
ERR_ENCRYPTION_REQUIRED,
ERR_INVALID_FORMAT,
ERR_SENSOR_DUPLICATE_UNIQUE_ID,
ERR_SENSOR_NOT_REGISTERED,
SIGNAL_LOCATION_UPDATE,
SIGNAL_SENSOR_UPDATE,
@@ -95,6 +95,7 @@ from .helpers import (
_LOGGER = logging.getLogger(__name__)
DELAY_SAVE = 10
WEBHOOK_COMMANDS = Registry()
@@ -184,7 +185,10 @@ async def handle_webhook(
"Received webhook payload for type %s: %s", webhook_type, webhook_payload
)
return await WEBHOOK_COMMANDS[webhook_type](hass, config_entry, webhook_payload)
# Shield so we make sure we finish the webhook, even if sender hangs up.
return await asyncio.shield(
WEBHOOK_COMMANDS[webhook_type](hass, config_entry, webhook_payload)
)
@WEBHOOK_COMMANDS.register("call_service")
@@ -352,38 +356,37 @@ async def webhook_enable_encryption(hass, config_entry, data):
vol.Required(ATTR_SENSOR_TYPE): vol.In(SENSOR_TYPES),
vol.Required(ATTR_SENSOR_UNIQUE_ID): cv.string,
vol.Optional(ATTR_SENSOR_UOM): cv.string,
vol.Required(ATTR_SENSOR_STATE): vol.Any(bool, str, int, float),
vol.Required(ATTR_SENSOR_STATE): vol.Any(None, bool, str, int, float),
vol.Optional(ATTR_SENSOR_ICON, default="mdi:cellphone"): cv.icon,
}
)
async def webhook_register_sensor(hass, config_entry, data):
"""Handle a register sensor webhook."""
entity_type = data[ATTR_SENSOR_TYPE]
unique_id = data[ATTR_SENSOR_UNIQUE_ID]
unique_store_key = f"{config_entry.data[CONF_WEBHOOK_ID]}_{unique_id}"
if unique_store_key in hass.data[DOMAIN][entity_type]:
_LOGGER.error("Refusing to re-register existing sensor %s!", unique_id)
return error_response(
ERR_SENSOR_DUPLICATE_UNIQUE_ID,
f"{entity_type} {unique_id} already exists!",
status=409,
)
existing_sensor = unique_store_key in hass.data[DOMAIN][entity_type]
data[CONF_WEBHOOK_ID] = config_entry.data[CONF_WEBHOOK_ID]
# If sensor already is registered, update current state instead
if existing_sensor:
_LOGGER.debug("Re-register existing sensor %s", unique_id)
entry = hass.data[DOMAIN][entity_type][unique_store_key]
data = {**entry, **data}
hass.data[DOMAIN][entity_type][unique_store_key] = data
try:
await hass.data[DOMAIN][DATA_STORE].async_save(savable_state(hass))
except HomeAssistantError as ex:
_LOGGER.error("Error registering sensor: %s", ex)
return empty_okay_response()
hass.data[DOMAIN][DATA_STORE].async_delay_save(
lambda: savable_state(hass), DELAY_SAVE
)
register_signal = f"{DOMAIN}_{data[ATTR_SENSOR_TYPE]}_register"
async_dispatcher_send(hass, register_signal, data)
if existing_sensor:
async_dispatcher_send(hass, SIGNAL_SENSOR_UPDATE, data)
else:
register_signal = f"{DOMAIN}_{data[ATTR_SENSOR_TYPE]}_register"
async_dispatcher_send(hass, register_signal, data)
return webhook_response(
{"success": True}, registration=config_entry.data, status=HTTP_CREATED,
@@ -414,7 +417,7 @@ async def webhook_update_sensor_states(hass, config_entry, data):
{
vol.Optional(ATTR_SENSOR_ATTRIBUTES, default={}): dict,
vol.Optional(ATTR_SENSOR_ICON, default="mdi:cellphone"): cv.icon,
vol.Required(ATTR_SENSOR_STATE): vol.Any(bool, str, int, float),
vol.Required(ATTR_SENSOR_STATE): vol.Any(None, bool, str, int, float),
vol.Required(ATTR_SENSOR_TYPE): vol.In(SENSOR_TYPES),
vol.Required(ATTR_SENSOR_UNIQUE_ID): cv.string,
}
@@ -458,18 +461,14 @@ async def webhook_update_sensor_states(hass, config_entry, data):
hass.data[DOMAIN][entity_type][unique_store_key] = new_state
safe = savable_state(hass)
try:
await hass.data[DOMAIN][DATA_STORE].async_save(safe)
except HomeAssistantError as ex:
_LOGGER.error("Error updating mobile_app registration: %s", ex)
return empty_okay_response()
async_dispatcher_send(hass, SIGNAL_SENSOR_UPDATE, new_state)
resp[unique_id] = {"success": True}
hass.data[DOMAIN][DATA_STORE].async_delay_save(
lambda: savable_state(hass), DELAY_SAVE
)
return webhook_response(resp, registration=config_entry.data)

View File

@@ -2,7 +2,7 @@
"domain": "ssdp",
"name": "Simple Service Discovery Protocol (SSDP)",
"documentation": "https://www.home-assistant.io/integrations/ssdp",
"requirements": ["defusedxml==0.6.0", "netdisco==2.6.0"],
"requirements": ["defusedxml==0.6.0", "netdisco==2.7.0"],
"after_dependencies": ["zeroconf"],
"codeowners": []
}

View File

@@ -83,20 +83,78 @@ async def async_setup_entry(
update_segments()
class WLEDLight(LightEntity, WLEDDeviceEntity):
"""Defines a WLED light."""
class WLEDMasterLight(LightEntity, WLEDDeviceEntity):
"""Defines a WLED master light."""
def __init__(self, entry_id: str, coordinator: WLEDDataUpdateCoordinator):
"""Initialize WLED master light."""
super().__init__(
entry_id=entry_id,
coordinator=coordinator,
name=f"{coordinator.data.info.name} Master",
icon="mdi:led-strip-variant",
)
@property
def unique_id(self) -> str:
"""Return the unique ID for this sensor."""
return f"{self.coordinator.data.info.mac_address}"
@property
def supported_features(self) -> int:
"""Flag supported features."""
return SUPPORT_BRIGHTNESS | SUPPORT_TRANSITION
@property
def brightness(self) -> Optional[int]:
"""Return the brightness of this light between 1..255."""
return self.coordinator.data.state.brightness
@property
def is_on(self) -> bool:
"""Return the state of the light."""
return bool(self.coordinator.data.state.on)
@wled_exception_handler
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off the light."""
data = {ATTR_ON: False}
if ATTR_TRANSITION in kwargs:
# WLED uses 100ms per unit, so 10 = 1 second.
data[ATTR_TRANSITION] = round(kwargs[ATTR_TRANSITION] * 10)
await self.coordinator.wled.master(**data)
@wled_exception_handler
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on the light."""
data = {ATTR_ON: True}
if ATTR_TRANSITION in kwargs:
# WLED uses 100ms per unit, so 10 = 1 second.
data[ATTR_TRANSITION] = round(kwargs[ATTR_TRANSITION] * 10)
if ATTR_BRIGHTNESS in kwargs:
data[ATTR_BRIGHTNESS] = kwargs[ATTR_BRIGHTNESS]
await self.coordinator.wled.master(**data)
class WLEDSegmentLight(LightEntity, WLEDDeviceEntity):
"""Defines a WLED light based on a segment."""
def __init__(
self, entry_id: str, coordinator: WLEDDataUpdateCoordinator, segment: int
):
"""Initialize WLED light."""
"""Initialize WLED segment light."""
self._rgbw = coordinator.data.info.leds.rgbw
self._segment = segment
# Only apply the segment ID if it is not the first segment
name = coordinator.data.info.name
if segment != 0:
name += f" {segment}"
# If this is the one and only segment, use a simpler name
name = f"{coordinator.data.info.name} Segment {self._segment}"
if len(coordinator.data.state.segments) == 1:
name = coordinator.data.info.name
super().__init__(
entry_id=entry_id,
@@ -155,7 +213,16 @@ class WLEDLight(LightEntity, WLEDDeviceEntity):
@property
def brightness(self) -> Optional[int]:
"""Return the brightness of this light between 1..255."""
return self.coordinator.data.state.brightness
state = self.coordinator.data.state
# If this is the one and only segment, calculate brightness based
# on the master and segment brightness
if len(state.segments) == 1:
return int(
(state.segments[self._segment].brightness * state.brightness) / 255
)
return state.segments[self._segment].brightness
@property
def white_value(self) -> Optional[int]:
@@ -187,18 +254,30 @@ class WLEDLight(LightEntity, WLEDDeviceEntity):
@property
def is_on(self) -> bool:
"""Return the state of the light."""
return bool(self.coordinator.data.state.on)
state = self.coordinator.data.state
# If there is a single segment, take master into account
if len(state.segments) == 1 and not state.on:
return False
return bool(state.segments[self._segment].on)
@wled_exception_handler
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off the light."""
data = {ATTR_ON: False, ATTR_SEGMENT_ID: self._segment}
data = {ATTR_ON: False}
if ATTR_TRANSITION in kwargs:
# WLED uses 100ms per unit, so 10 = 1 second.
data[ATTR_TRANSITION] = round(kwargs[ATTR_TRANSITION] * 10)
await self.coordinator.wled.light(**data)
# If there is a single segment, control via the master
if len(self.coordinator.data.state.segments) == 1:
await self.coordinator.wled.master(**data)
return
data[ATTR_SEGMENT_ID] = self._segment
await self.coordinator.wled.segment(**data)
@wled_exception_handler
async def async_turn_on(self, **kwargs: Any) -> None:
@@ -248,7 +327,23 @@ class WLEDLight(LightEntity, WLEDDeviceEntity):
else:
data[ATTR_COLOR_PRIMARY] += (self.white_value,)
await self.coordinator.wled.light(**data)
# When only 1 segment is present, switch along the master, and use
# the master for power/brightness control.
if len(self.coordinator.data.state.segments) == 1:
master_data = {ATTR_ON: True}
if ATTR_BRIGHTNESS in data:
master_data[ATTR_BRIGHTNESS] = data[ATTR_BRIGHTNESS]
data[ATTR_BRIGHTNESS] = 255
if ATTR_TRANSITION in data:
master_data[ATTR_TRANSITION] = data[ATTR_TRANSITION]
del data[ATTR_TRANSITION]
await self.coordinator.wled.segment(**data)
await self.coordinator.wled.master(**master_data)
return
await self.coordinator.wled.segment(**data)
@wled_exception_handler
async def async_effect(
@@ -273,45 +368,59 @@ class WLEDLight(LightEntity, WLEDDeviceEntity):
if speed is not None:
data[ATTR_SPEED] = speed
await self.coordinator.wled.light(**data)
await self.coordinator.wled.segment(**data)
@callback
def async_update_segments(
entry: ConfigEntry,
coordinator: WLEDDataUpdateCoordinator,
current: Dict[int, WLEDLight],
current: Dict[int, WLEDSegmentLight],
async_add_entities,
) -> None:
"""Update segments."""
segment_ids = {light.segment_id for light in coordinator.data.state.segments}
current_ids = set(current)
# Process new segments, add them to Home Assistant
new_segments = []
for segment_id in segment_ids - current_ids:
current[segment_id] = WLEDLight(entry.entry_id, coordinator, segment_id)
new_segments.append(current[segment_id])
# Discard master (if present)
current_ids.discard(-1)
if new_segments:
async_add_entities(new_segments)
# Process new segments, add them to Home Assistant
new_entities = []
for segment_id in segment_ids - current_ids:
current[segment_id] = WLEDSegmentLight(entry.entry_id, coordinator, segment_id)
new_entities.append(current[segment_id])
# More than 1 segment now? Add master controls
if len(current_ids) < 2 and len(segment_ids) > 1:
current[-1] = WLEDMasterLight(entry.entry_id, coordinator)
new_entities.append(current[-1])
if new_entities:
async_add_entities(new_entities)
# Process deleted segments, remove them from Home Assistant
for segment_id in current_ids - segment_ids:
coordinator.hass.async_create_task(
async_remove_segment(segment_id, coordinator, current)
async_remove_entity(segment_id, coordinator, current)
)
# Remove master if there is only 1 segment left
if len(current_ids) > 1 and len(segment_ids) < 2:
coordinator.hass.async_create_task(
async_remove_entity(-1, coordinator, current)
)
async def async_remove_segment(
segment_id: int,
async def async_remove_entity(
index: int,
coordinator: WLEDDataUpdateCoordinator,
current: Dict[int, WLEDLight],
current: Dict[int, WLEDSegmentLight],
) -> None:
"""Remove WLED segment light from Home Assistant."""
entity = current[segment_id]
entity = current[index]
await entity.async_remove()
registry = await async_get_entity_registry(coordinator.hass)
if entity.entity_id in registry.entities:
registry.async_remove(entity.entity_id)
del current[segment_id]
del current[index]

View File

@@ -3,7 +3,7 @@
"name": "WLED",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/wled",
"requirements": ["wled==0.4.1"],
"requirements": ["wled==0.4.2"],
"zeroconf": ["_wled._tcp.local."],
"codeowners": ["@frenck"],
"quality_scale": "platinum"

View File

@@ -7,7 +7,7 @@
"bellows==0.16.2",
"pyserial==3.4",
"zha-quirks==0.0.39",
"zigpy-cc==0.4.2",
"zigpy-cc==0.4.4",
"zigpy-deconz==0.9.2",
"zigpy==0.20.4",
"zigpy-xbee==0.12.1",

View File

@@ -1,7 +1,7 @@
"""Constants used by Home Assistant components."""
MAJOR_VERSION = 0
MINOR_VERSION = 111
PATCH_VERSION = "0b2"
PATCH_VERSION = "0b4"
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__ = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER = (3, 7, 0)

View File

@@ -12,10 +12,10 @@ cryptography==2.9.2
defusedxml==0.6.0
distro==1.5.0
hass-nabucasa==0.34.5
home-assistant-frontend==20200603.1
home-assistant-frontend==20200603.2
importlib-metadata==1.6.0
jinja2>=2.11.1
netdisco==2.6.0
netdisco==2.7.0
pip>=8.0.3
python-slugify==4.0.0
pytz>=2020.1

View File

@@ -734,7 +734,7 @@ hole==0.5.1
holidays==0.10.2
# homeassistant.components.frontend
home-assistant-frontend==20200603.1
home-assistant-frontend==20200603.2
# homeassistant.components.zwave
homeassistant-pyozw==0.1.10
@@ -946,7 +946,7 @@ netdata==0.1.2
# homeassistant.components.discovery
# homeassistant.components.ssdp
netdisco==2.6.0
netdisco==2.7.0
# homeassistant.components.neurio_energy
neurio==0.3.1
@@ -1245,7 +1245,7 @@ pycfdns==0.0.1
pychannels==1.0.0
# homeassistant.components.cast
pychromecast==5.3.0
pychromecast==6.0.0
# homeassistant.components.cmus
pycmus==0.1.1
@@ -2195,7 +2195,7 @@ wirelesstagpy==0.4.0
withings-api==2.1.3
# homeassistant.components.wled
wled==0.4.1
wled==0.4.2
# homeassistant.components.xbee
xbee-helper==0.0.7
@@ -2251,7 +2251,7 @@ zhong_hong_hvac==1.0.9
ziggo-mediabox-xl==1.1.0
# homeassistant.components.zha
zigpy-cc==0.4.2
zigpy-cc==0.4.4
# homeassistant.components.zha
zigpy-deconz==0.9.2

View File

@@ -321,7 +321,7 @@ hole==0.5.1
holidays==0.10.2
# homeassistant.components.frontend
home-assistant-frontend==20200603.1
home-assistant-frontend==20200603.2
# homeassistant.components.zwave
homeassistant-pyozw==0.1.10
@@ -399,7 +399,7 @@ nessclient==0.9.15
# homeassistant.components.discovery
# homeassistant.components.ssdp
netdisco==2.6.0
netdisco==2.7.0
# homeassistant.components.nexia
nexia==0.9.3
@@ -539,7 +539,7 @@ pyblackbird==0.5
pybotvac==0.0.17
# homeassistant.components.cast
pychromecast==5.3.0
pychromecast==6.0.0
# homeassistant.components.coolmaster
pycoolmasternet==0.0.4
@@ -901,7 +901,7 @@ wiffi==1.0.0
withings-api==2.1.3
# homeassistant.components.wled
wled==0.4.1
wled==0.4.2
# homeassistant.components.bluesound
# homeassistant.components.rest
@@ -921,7 +921,7 @@ zeroconf==0.27.1
zha-quirks==0.0.39
# homeassistant.components.zha
zigpy-cc==0.4.2
zigpy-cc==0.4.4
# homeassistant.components.zha
zigpy-deconz==0.9.2

View File

@@ -80,17 +80,17 @@ async def async_setup_cast_internal_discovery(hass, config=None, discovery_info=
browser = MagicMock(zc={})
with patch(
"homeassistant.components.cast.discovery.pychromecast.discovery.CastListener",
"homeassistant.components.cast.discovery.pychromecast.CastListener",
return_value=listener,
) as cast_listener, patch(
"homeassistant.components.cast.discovery.zeroconf.ServiceBrowser",
"homeassistant.components.cast.discovery.pychromecast.start_discovery",
return_value=browser,
):
) as start_discovery:
add_entities = await async_setup_cast(hass, config, discovery_info)
await hass.async_block_till_done()
await hass.async_block_till_done()
assert cast_listener.call_count == 1
assert start_discovery.call_count == 1
discovery_callback = cast_listener.call_args[0][0]
@@ -120,10 +120,10 @@ async def async_setup_media_player_cast(hass: HomeAssistantType, info: Chromecas
"homeassistant.components.cast.discovery.pychromecast.get_chromecast_from_service",
return_value=chromecast,
) as get_chromecast, patch(
"homeassistant.components.cast.discovery.pychromecast.discovery.CastListener",
"homeassistant.components.cast.discovery.pychromecast.CastListener",
return_value=listener,
) as cast_listener, patch(
"homeassistant.components.cast.discovery.zeroconf.ServiceBrowser",
"homeassistant.components.cast.discovery.pychromecast.start_discovery",
return_value=browser,
):
await async_setup_component(
@@ -159,17 +159,15 @@ async def async_setup_media_player_cast(hass: HomeAssistantType, info: Chromecas
async def test_start_discovery_called_once(hass):
"""Test pychromecast.start_discovery called exactly once."""
with patch(
"homeassistant.components.cast.discovery.pychromecast.discovery.CastListener",
) as cast_listener, patch(
"homeassistant.components.cast.discovery.zeroconf.ServiceBrowser",
"homeassistant.components.cast.discovery.pychromecast.start_discovery",
return_value=Mock(),
):
) as start_discovery:
await async_setup_cast(hass)
assert cast_listener.call_count == 1
assert start_discovery.call_count == 1
await async_setup_cast(hass)
assert cast_listener.call_count == 1
assert start_discovery.call_count == 1
async def test_stop_discovery_called_on_stop(hass):
@@ -177,15 +175,13 @@ async def test_stop_discovery_called_on_stop(hass):
browser = MagicMock(zc={})
with patch(
"homeassistant.components.cast.discovery.pychromecast.discovery.CastListener",
) as cast_listener, patch(
"homeassistant.components.cast.discovery.zeroconf.ServiceBrowser",
"homeassistant.components.cast.discovery.pychromecast.start_discovery",
return_value=browser,
):
) as start_discovery:
# start_discovery should be called with empty config
await async_setup_cast(hass, {})
assert cast_listener.call_count == 1
assert start_discovery.call_count == 1
with patch(
"homeassistant.components.cast.discovery.pychromecast.stop_discovery"
@@ -197,15 +193,13 @@ async def test_stop_discovery_called_on_stop(hass):
stop_discovery.assert_called_once_with(browser)
with patch(
"homeassistant.components.cast.discovery.pychromecast.discovery.CastListener",
) as cast_listener, patch(
"homeassistant.components.cast.discovery.zeroconf.ServiceBrowser",
"homeassistant.components.cast.discovery.pychromecast.start_discovery",
return_value=browser,
):
) as start_discovery:
# start_discovery should be called again on re-startup
await async_setup_cast(hass)
assert cast_listener.call_count == 1
assert start_discovery.call_count == 1
async def test_create_cast_device_without_uuid(hass):

View File

@@ -1,12 +1,13 @@
"""Fixtures for tests."""
from mock import patch
import pytest
from homeassistant.core import HomeAssistant
from .common import ComponentFactory
from tests.async_mock import patch
@pytest.fixture()
def component_factory(hass: HomeAssistant):

View File

@@ -4,8 +4,6 @@ import os
import shutil
from urllib.parse import urlencode
from mock import Mock, patch
from homeassistant.components.media_player.const import (
ATTR_MEDIA_CONTENT_ID,
DOMAIN as DOMAIN_MP,
@@ -16,6 +14,7 @@ from homeassistant.config import async_process_ha_core_config
from homeassistant.const import HTTP_INTERNAL_SERVER_ERROR
from homeassistant.setup import setup_component
from tests.async_mock import Mock, patch
from tests.common import assert_setup_component, get_test_home_assistant, mock_service

View File

@@ -2,7 +2,7 @@
import logging
from homeassistant.const import UNIT_PERCENTAGE
from homeassistant.const import STATE_UNKNOWN, UNIT_PERCENTAGE
from homeassistant.helpers import device_registry
_LOGGER = logging.getLogger(__name__)
@@ -95,8 +95,8 @@ async def test_sensor_must_register(hass, create_registrations, webhook_client):
assert json["battery_state"]["error"]["code"] == "not_registered"
async def test_sensor_id_no_dupes(hass, create_registrations, webhook_client):
"""Test that sensors must have a unique ID."""
async def test_sensor_id_no_dupes(hass, create_registrations, webhook_client, caplog):
"""Test that a duplicate unique ID in registration updates the sensor."""
webhook_id = create_registrations[1]["webhook_id"]
webhook_url = f"/api/webhook/{webhook_id}"
@@ -120,11 +120,115 @@ async def test_sensor_id_no_dupes(hass, create_registrations, webhook_client):
reg_json = await reg_resp.json()
assert reg_json == {"success": True}
await hass.async_block_till_done()
assert "Re-register existing sensor" not in caplog.text
entity = hass.states.get("sensor.test_1_battery_state")
assert entity is not None
assert entity.attributes["device_class"] == "battery"
assert entity.attributes["icon"] == "mdi:battery"
assert entity.attributes["unit_of_measurement"] == UNIT_PERCENTAGE
assert entity.attributes["foo"] == "bar"
assert entity.domain == "sensor"
assert entity.name == "Test 1 Battery State"
assert entity.state == "100"
payload["data"]["state"] = 99
dupe_resp = await webhook_client.post(webhook_url, json=payload)
assert dupe_resp.status == 409
assert dupe_resp.status == 201
dupe_reg_json = await dupe_resp.json()
assert dupe_reg_json == {"success": True}
await hass.async_block_till_done()
dupe_json = await dupe_resp.json()
assert dupe_json["success"] is False
assert dupe_json["error"]["code"] == "duplicate_unique_id"
assert "Re-register existing sensor" in caplog.text
entity = hass.states.get("sensor.test_1_battery_state")
assert entity is not None
assert entity.attributes["device_class"] == "battery"
assert entity.attributes["icon"] == "mdi:battery"
assert entity.attributes["unit_of_measurement"] == UNIT_PERCENTAGE
assert entity.attributes["foo"] == "bar"
assert entity.domain == "sensor"
assert entity.name == "Test 1 Battery State"
assert entity.state == "99"
async def test_register_sensor_no_state(hass, create_registrations, webhook_client):
"""Test that sensors can be registered, when there is no (unknown) state."""
webhook_id = create_registrations[1]["webhook_id"]
webhook_url = f"/api/webhook/{webhook_id}"
reg_resp = await webhook_client.post(
webhook_url,
json={
"type": "register_sensor",
"data": {
"name": "Battery State",
"state": None,
"type": "sensor",
"unique_id": "battery_state",
},
},
)
assert reg_resp.status == 201
json = await reg_resp.json()
assert json == {"success": True}
await hass.async_block_till_done()
entity = hass.states.get("sensor.test_1_battery_state")
assert entity is not None
assert entity.domain == "sensor"
assert entity.name == "Test 1 Battery State"
assert entity.state == STATE_UNKNOWN
async def test_update_sensor_no_state(hass, create_registrations, webhook_client):
"""Test that sensors can be updated, when there is no (unknown) state."""
webhook_id = create_registrations[1]["webhook_id"]
webhook_url = f"/api/webhook/{webhook_id}"
reg_resp = await webhook_client.post(
webhook_url,
json={
"type": "register_sensor",
"data": {
"name": "Battery State",
"state": 100,
"type": "sensor",
"unique_id": "battery_state",
},
},
)
assert reg_resp.status == 201
json = await reg_resp.json()
assert json == {"success": True}
await hass.async_block_till_done()
entity = hass.states.get("sensor.test_1_battery_state")
assert entity is not None
assert entity.state == "100"
update_resp = await webhook_client.post(
webhook_url,
json={
"type": "update_sensor_states",
"data": [{"state": None, "type": "sensor", "unique_id": "battery_state"}],
},
)
assert update_resp.status == 200
json = await update_resp.json()
assert json == {"battery_state": {"success": True}}
updated_entity = hass.states.get("sensor.test_1_battery_state")
assert updated_entity.state == STATE_UNKNOWN

View File

@@ -2,7 +2,6 @@
import datetime
from typing import Union
import mock
from py17track.package import Package
import pytest
@@ -14,6 +13,7 @@ from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.setup import async_setup_component
from homeassistant.util import utcnow
from tests.async_mock import MagicMock, patch
from tests.common import async_fire_time_changed
VALID_CONFIG_MINIMAL = {
@@ -113,7 +113,7 @@ class ProfileMock:
@pytest.fixture(autouse=True, name="mock_client")
def fixture_mock_client():
"""Mock py17track client."""
with mock.patch(
with patch(
"homeassistant.components.seventeentrack.sensor.SeventeenTrackClient",
new=ClientMock,
):
@@ -137,7 +137,7 @@ async def _goto_future(hass, future=None):
"""Move to future."""
if not future:
future = utcnow() + datetime.timedelta(minutes=10)
with mock.patch("homeassistant.util.utcnow", return_value=future):
with patch("homeassistant.util.utcnow", return_value=future):
async_fire_time_changed(hass, future)
await hass.async_block_till_done()
@@ -245,7 +245,7 @@ async def test_delivered_not_shown(hass):
)
ProfileMock.package_list = [package]
hass.components.persistent_notification = mock.MagicMock()
hass.components.persistent_notification = MagicMock()
await _setup_seventeentrack(hass, VALID_CONFIG_FULL_NO_DELIVERED)
assert not hass.states.async_entity_ids()
hass.components.persistent_notification.create.assert_called()
@@ -258,7 +258,7 @@ async def test_delivered_shown(hass):
)
ProfileMock.package_list = [package]
hass.components.persistent_notification = mock.MagicMock()
hass.components.persistent_notification = MagicMock()
await _setup_seventeentrack(hass, VALID_CONFIG_FULL)
assert hass.states.get("sensor.seventeentrack_package_456") is not None
@@ -283,7 +283,7 @@ async def test_becomes_delivered_not_shown_notification(hass):
)
ProfileMock.package_list = [package_delivered]
hass.components.persistent_notification = mock.MagicMock()
hass.components.persistent_notification = MagicMock()
await _goto_future(hass)
hass.components.persistent_notification.create.assert_called()

View File

@@ -2,13 +2,13 @@
from typing import Callable, Dict, NamedTuple, Tuple
from mock import MagicMock
import pyvera as pv
from homeassistant.components.vera.const import CONF_CONTROLLER, DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from tests.async_mock import MagicMock
from tests.common import MockConfigEntry
SetupCallback = Callable[[pv.VeraController, dict], None]

View File

@@ -1,10 +1,11 @@
"""Fixtures for tests."""
from mock import patch
import pytest
from .common import ComponentFactory
from tests.async_mock import patch
@pytest.fixture()
def vera_component_factory():

View File

@@ -1,5 +1,4 @@
"""Vera tests."""
from mock import patch
from requests.exceptions import RequestException
from homeassistant import config_entries, data_entry_flow
@@ -12,7 +11,7 @@ from homeassistant.data_entry_flow import (
RESULT_TYPE_FORM,
)
from tests.async_mock import MagicMock
from tests.async_mock import MagicMock, patch
from tests.common import MockConfigEntry

View File

@@ -29,6 +29,7 @@ from homeassistant.const import (
ATTR_ICON,
SERVICE_TURN_OFF,
SERVICE_TURN_ON,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
)
@@ -50,7 +51,7 @@ async def test_rgb_light_state(
entity_registry = await hass.helpers.entity_registry.async_get_registry()
# First segment of the strip
state = hass.states.get("light.wled_rgb_light")
state = hass.states.get("light.wled_rgb_light_segment_0")
assert state
assert state.attributes.get(ATTR_BRIGHTNESS) == 127
assert state.attributes.get(ATTR_EFFECT) == "Solid"
@@ -64,12 +65,12 @@ async def test_rgb_light_state(
assert state.attributes.get(ATTR_SPEED) == 32
assert state.state == STATE_ON
entry = entity_registry.async_get("light.wled_rgb_light")
entry = entity_registry.async_get("light.wled_rgb_light_segment_0")
assert entry
assert entry.unique_id == "aabbccddeeff_0"
# Second segment of the strip
state = hass.states.get("light.wled_rgb_light_1")
state = hass.states.get("light.wled_rgb_light_segment_1")
assert state
assert state.attributes.get(ATTR_BRIGHTNESS) == 127
assert state.attributes.get(ATTR_EFFECT) == "Blink"
@@ -83,22 +84,32 @@ async def test_rgb_light_state(
assert state.attributes.get(ATTR_SPEED) == 16
assert state.state == STATE_ON
entry = entity_registry.async_get("light.wled_rgb_light_1")
entry = entity_registry.async_get("light.wled_rgb_light_segment_1")
assert entry
assert entry.unique_id == "aabbccddeeff_1"
# Test master control of the lightstrip
state = hass.states.get("light.wled_rgb_light_master")
assert state
assert state.attributes.get(ATTR_BRIGHTNESS) == 127
assert state.state == STATE_ON
async def test_switch_change_state(
entry = entity_registry.async_get("light.wled_rgb_light_master")
assert entry
assert entry.unique_id == "aabbccddeeff"
async def test_segment_change_state(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, caplog
) -> None:
"""Test the change of state of the WLED switches."""
"""Test the change of state of the WLED segments."""
await init_integration(hass, aioclient_mock)
with patch("wled.WLED.light") as light_mock:
with patch("wled.WLED.segment") as light_mock:
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: "light.wled_rgb_light", ATTR_TRANSITION: 5},
{ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0", ATTR_TRANSITION: 5},
blocking=True,
)
await hass.async_block_till_done()
@@ -106,14 +117,14 @@ async def test_switch_change_state(
on=False, segment_id=0, transition=50,
)
with patch("wled.WLED.light") as light_mock:
with patch("wled.WLED.segment") as light_mock:
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{
ATTR_BRIGHTNESS: 42,
ATTR_EFFECT: "Chase",
ATTR_ENTITY_ID: "light.wled_rgb_light",
ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0",
ATTR_RGB_COLOR: [255, 0, 0],
ATTR_TRANSITION: 5,
},
@@ -129,11 +140,11 @@ async def test_switch_change_state(
transition=50,
)
with patch("wled.WLED.light") as light_mock:
with patch("wled.WLED.segment") as light_mock:
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{ATTR_ENTITY_ID: "light.wled_rgb_light", ATTR_COLOR_TEMP: 400},
{ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0", ATTR_COLOR_TEMP: 400},
blocking=True,
)
await hass.async_block_till_done()
@@ -142,33 +153,178 @@ async def test_switch_change_state(
)
async def test_master_change_state(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, caplog
) -> None:
"""Test the change of state of the WLED master light control."""
await init_integration(hass, aioclient_mock)
with patch("wled.WLED.master") as light_mock:
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: "light.wled_rgb_light_master", ATTR_TRANSITION: 5},
blocking=True,
)
await hass.async_block_till_done()
light_mock.assert_called_once_with(
on=False, transition=50,
)
with patch("wled.WLED.master") as light_mock:
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{
ATTR_BRIGHTNESS: 42,
ATTR_ENTITY_ID: "light.wled_rgb_light_master",
ATTR_TRANSITION: 5,
},
blocking=True,
)
await hass.async_block_till_done()
light_mock.assert_called_once_with(
brightness=42, on=True, transition=50,
)
with patch("wled.WLED.master") as light_mock:
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: "light.wled_rgb_light_master", ATTR_TRANSITION: 5},
blocking=True,
)
await hass.async_block_till_done()
light_mock.assert_called_once_with(
on=False, transition=50,
)
with patch("wled.WLED.master") as light_mock:
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{
ATTR_BRIGHTNESS: 42,
ATTR_ENTITY_ID: "light.wled_rgb_light_master",
ATTR_TRANSITION: 5,
},
blocking=True,
)
await hass.async_block_till_done()
light_mock.assert_called_once_with(
brightness=42, on=True, transition=50,
)
async def test_dynamically_handle_segments(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
) -> None:
"""Test if a new/deleted segment is dynamically added/removed."""
await init_integration(hass, aioclient_mock)
assert hass.states.get("light.wled_rgb_light")
assert hass.states.get("light.wled_rgb_light_1")
assert hass.states.get("light.wled_rgb_light_master")
assert hass.states.get("light.wled_rgb_light_segment_0")
assert hass.states.get("light.wled_rgb_light_segment_1")
data = json.loads(load_fixture("wled/rgb_single_segment.json"))
device = WLEDDevice(data)
# Test removal if segment went missing
# Test removal if segment went missing, including the master entity
with patch(
"homeassistant.components.wled.WLED.update", return_value=device,
):
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL)
await hass.async_block_till_done()
assert hass.states.get("light.wled_rgb_light")
assert not hass.states.get("light.wled_rgb_light_1")
assert hass.states.get("light.wled_rgb_light_segment_0")
assert not hass.states.get("light.wled_rgb_light_segment_1")
assert not hass.states.get("light.wled_rgb_light_master")
# Test adding if segment shows up again
# Test adding if segment shows up again, including the master entity
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL)
await hass.async_block_till_done()
assert hass.states.get("light.wled_rgb_light")
assert hass.states.get("light.wled_rgb_light_1")
assert hass.states.get("light.wled_rgb_light_master")
assert hass.states.get("light.wled_rgb_light_segment_0")
assert hass.states.get("light.wled_rgb_light_segment_1")
async def test_single_segment_behavior(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, caplog
) -> None:
"""Test the behavior of the integration with a single segment."""
await init_integration(hass, aioclient_mock)
data = json.loads(load_fixture("wled/rgb_single_segment.json"))
device = WLEDDevice(data)
# Test absent master
with patch(
"homeassistant.components.wled.WLED.update", return_value=device,
):
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL)
await hass.async_block_till_done()
assert not hass.states.get("light.wled_rgb_light_master")
state = hass.states.get("light.wled_rgb_light_segment_0")
assert state
assert state.state == STATE_ON
# Test segment brightness takes master into account
device.state.brightness = 100
device.state.segments[0].brightness = 255
with patch(
"homeassistant.components.wled.WLED.update", return_value=device,
):
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL)
await hass.async_block_till_done()
state = hass.states.get("light.wled_rgb_light_segment_0")
assert state
assert state.attributes.get(ATTR_BRIGHTNESS) == 100
# Test segment is off when master is off
device.state.on = False
with patch(
"homeassistant.components.wled.WLED.update", return_value=device,
):
async_fire_time_changed(hass, dt_util.utcnow() + SCAN_INTERVAL)
await hass.async_block_till_done()
state = hass.states.get("light.wled_rgb_light_segment_0")
assert state
assert state.state == STATE_OFF
# Test master is turned off when turning off a single segment
with patch("wled.WLED.master") as master_mock:
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0", ATTR_TRANSITION: 5},
blocking=True,
)
await hass.async_block_till_done()
master_mock.assert_called_once_with(
on=False, transition=50,
)
# Test master is turned on when turning on a single segment, and segment
# brightness is set to 255.
with patch("wled.WLED.master") as master_mock, patch(
"wled.WLED.segment"
) as segment_mock:
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
{
ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0",
ATTR_TRANSITION: 5,
ATTR_BRIGHTNESS: 42,
},
blocking=True,
)
await hass.async_block_till_done()
master_mock.assert_called_once_with(on=True, transition=50, brightness=42)
segment_mock.assert_called_once_with(on=True, segment_id=0, brightness=255)
async def test_light_error(
@@ -182,12 +338,12 @@ async def test_light_error(
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: "light.wled_rgb_light"},
{ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0"},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get("light.wled_rgb_light")
state = hass.states.get("light.wled_rgb_light_segment_0")
assert state.state == STATE_ON
assert "Invalid response from API" in caplog.text
@@ -199,17 +355,17 @@ async def test_light_connection_error(
await init_integration(hass, aioclient_mock)
with patch("homeassistant.components.wled.WLED.update"), patch(
"homeassistant.components.wled.WLED.light", side_effect=WLEDConnectionError
"homeassistant.components.wled.WLED.segment", side_effect=WLEDConnectionError
):
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_OFF,
{ATTR_ENTITY_ID: "light.wled_rgb_light"},
{ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0"},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get("light.wled_rgb_light")
state = hass.states.get("light.wled_rgb_light_segment_0")
assert state.state == STATE_UNAVAILABLE
@@ -224,7 +380,7 @@ async def test_rgbw_light(
assert state.attributes.get(ATTR_HS_COLOR) == (0.0, 100.0)
assert state.attributes.get(ATTR_WHITE_VALUE) == 139
with patch("wled.WLED.light") as light_mock:
with patch("wled.WLED.segment") as light_mock:
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
@@ -236,7 +392,7 @@ async def test_rgbw_light(
on=True, segment_id=0, color_primary=(255, 159, 70, 139),
)
with patch("wled.WLED.light") as light_mock:
with patch("wled.WLED.segment") as light_mock:
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
@@ -248,7 +404,7 @@ async def test_rgbw_light(
color_primary=(255, 0, 0, 100), on=True, segment_id=0,
)
with patch("wled.WLED.light") as light_mock:
with patch("wled.WLED.segment") as light_mock:
await hass.services.async_call(
LIGHT_DOMAIN,
SERVICE_TURN_ON,
@@ -271,13 +427,13 @@ async def test_effect_service(
"""Test the effect service of a WLED light."""
await init_integration(hass, aioclient_mock)
with patch("wled.WLED.light") as light_mock:
with patch("wled.WLED.segment") as light_mock:
await hass.services.async_call(
DOMAIN,
SERVICE_EFFECT,
{
ATTR_EFFECT: "Rainbow",
ATTR_ENTITY_ID: "light.wled_rgb_light",
ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0",
ATTR_INTENSITY: 200,
ATTR_REVERSE: True,
ATTR_SPEED: 100,
@@ -289,11 +445,11 @@ async def test_effect_service(
effect="Rainbow", intensity=200, reverse=True, segment_id=0, speed=100,
)
with patch("wled.WLED.light") as light_mock:
with patch("wled.WLED.segment") as light_mock:
await hass.services.async_call(
DOMAIN,
SERVICE_EFFECT,
{ATTR_ENTITY_ID: "light.wled_rgb_light", ATTR_EFFECT: 9},
{ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0", ATTR_EFFECT: 9},
blocking=True,
)
await hass.async_block_till_done()
@@ -301,12 +457,12 @@ async def test_effect_service(
segment_id=0, effect=9,
)
with patch("wled.WLED.light") as light_mock:
with patch("wled.WLED.segment") as light_mock:
await hass.services.async_call(
DOMAIN,
SERVICE_EFFECT,
{
ATTR_ENTITY_ID: "light.wled_rgb_light",
ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0",
ATTR_INTENSITY: 200,
ATTR_REVERSE: True,
ATTR_SPEED: 100,
@@ -318,13 +474,13 @@ async def test_effect_service(
intensity=200, reverse=True, segment_id=0, speed=100,
)
with patch("wled.WLED.light") as light_mock:
with patch("wled.WLED.segment") as light_mock:
await hass.services.async_call(
DOMAIN,
SERVICE_EFFECT,
{
ATTR_EFFECT: "Rainbow",
ATTR_ENTITY_ID: "light.wled_rgb_light",
ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0",
ATTR_REVERSE: True,
ATTR_SPEED: 100,
},
@@ -335,13 +491,13 @@ async def test_effect_service(
effect="Rainbow", reverse=True, segment_id=0, speed=100,
)
with patch("wled.WLED.light") as light_mock:
with patch("wled.WLED.segment") as light_mock:
await hass.services.async_call(
DOMAIN,
SERVICE_EFFECT,
{
ATTR_EFFECT: "Rainbow",
ATTR_ENTITY_ID: "light.wled_rgb_light",
ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0",
ATTR_INTENSITY: 200,
ATTR_SPEED: 100,
},
@@ -352,13 +508,13 @@ async def test_effect_service(
effect="Rainbow", intensity=200, segment_id=0, speed=100,
)
with patch("wled.WLED.light") as light_mock:
with patch("wled.WLED.segment") as light_mock:
await hass.services.async_call(
DOMAIN,
SERVICE_EFFECT,
{
ATTR_EFFECT: "Rainbow",
ATTR_ENTITY_ID: "light.wled_rgb_light",
ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0",
ATTR_INTENSITY: 200,
ATTR_REVERSE: True,
},
@@ -381,11 +537,11 @@ async def test_effect_service_error(
await hass.services.async_call(
DOMAIN,
SERVICE_EFFECT,
{ATTR_ENTITY_ID: "light.wled_rgb_light", ATTR_EFFECT: 9},
{ATTR_ENTITY_ID: "light.wled_rgb_light_segment_0", ATTR_EFFECT: 9},
blocking=True,
)
await hass.async_block_till_done()
state = hass.states.get("light.wled_rgb_light")
state = hass.states.get("light.wled_rgb_light_segment_0")
assert state.state == STATE_ON
assert "Invalid response from API" in caplog.text