Merge pull request #49302 from home-assistant/rc

pull/49313/head 2021.4.5
Franck Nijhof 2021-04-16 16:04:26 +02:00 committed by GitHub
commit 77f14b63f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 245 additions and 56 deletions

View File

@ -15,14 +15,14 @@ from .const import DOMAIN
PLATFORMS = ["sensor"]
async def async_setup(hass: HomeAssistant, config: dict):
async def async_setup(hass: HomeAssistant, config: dict) -> bool:
"""Set up the Coronavirus component."""
# Make sure coordinator is initialized.
await get_coordinator(hass)
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Coronavirus from a config entry."""
if isinstance(entry.data["country"], int):
hass.config_entries.async_update_entry(
@ -44,6 +44,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
if not entry.unique_id:
hass.config_entries.async_update_entry(entry, unique_id=entry.data["country"])
coordinator = await get_coordinator(hass)
if not coordinator.last_update_success:
await coordinator.async_config_entry_first_refresh()
for platform in PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, platform)
@ -52,9 +56,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
unload_ok = all(
return all(
await asyncio.gather(
*[
hass.config_entries.async_forward_entry_unload(entry, platform)
@ -63,10 +67,10 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
)
)
return unload_ok
async def get_coordinator(hass):
async def get_coordinator(
hass: HomeAssistant,
) -> update_coordinator.DataUpdateCoordinator:
"""Get the data update coordinator."""
if DOMAIN in hass.data:
return hass.data[DOMAIN]

View File

@ -1,4 +1,8 @@
"""Config flow for Coronavirus integration."""
from __future__ import annotations
from typing import Any
import voluptuous as vol
from homeassistant import config_entries
@ -15,13 +19,18 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
_options = None
async def async_step_user(self, user_input=None):
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> dict[str, Any]:
"""Handle the initial step."""
errors = {}
if self._options is None:
self._options = {OPTION_WORLDWIDE: "Worldwide"}
coordinator = await get_coordinator(self.hass)
if not coordinator.last_update_success:
return self.async_abort(reason="cannot_connect")
self._options = {OPTION_WORLDWIDE: "Worldwide"}
for case in sorted(
coordinator.data.values(), key=lambda case: case.country
):

View File

@ -7,6 +7,7 @@
}
},
"abort": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
}
}

View File

@ -1,7 +1,8 @@
{
"config": {
"abort": {
"already_configured": "Service is already configured"
"already_configured": "Service is already configured",
"cannot_connect": "Failed to connect"
},
"step": {
"user": {

View File

@ -3,7 +3,7 @@
"name": "DHCP Discovery",
"documentation": "https://www.home-assistant.io/integrations/dhcp",
"requirements": [
"scapy==2.4.4", "aiodiscover==1.3.3"
"scapy==2.4.4", "aiodiscover==1.3.4"
],
"codeowners": [
"@bdraco"

View File

@ -507,5 +507,5 @@ def state_needs_accessory_mode(state):
or state.domain == MEDIA_PLAYER_DOMAIN
and state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_TV
or state.domain == REMOTE_DOMAIN
and state.attributes.get(ATTR_SUPPORTED_FEATURES) & SUPPORT_ACTIVITY
and state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) & SUPPORT_ACTIVITY
)

View File

@ -751,3 +751,20 @@ class Light(LightEntity):
"Light is deprecated, modify %s to extend LightEntity",
cls.__name__,
)
def legacy_supported_features(
supported_features: int, supported_color_modes: list[str] | None
) -> int:
"""Calculate supported features with backwards compatibility."""
# Backwards compatibility for supported_color_modes added in 2021.4
if supported_color_modes is None:
return supported_features
if any(mode in supported_color_modes for mode in COLOR_MODES_COLOR):
supported_features |= SUPPORT_COLOR
if any(mode in supported_color_modes for mode in COLOR_MODES_BRIGHTNESS):
supported_features |= SUPPORT_BRIGHTNESS
if COLOR_MODE_COLOR_TEMP in supported_color_modes:
supported_features |= SUPPORT_COLOR_TEMP
return supported_features

View File

@ -35,6 +35,7 @@ from homeassistant.components.light import (
SUPPORT_WHITE_VALUE,
VALID_COLOR_MODES,
LightEntity,
legacy_supported_features,
valid_supported_color_modes,
)
from homeassistant.const import (
@ -458,7 +459,9 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity):
@property
def supported_features(self):
"""Flag supported features."""
return self._supported_features
return legacy_supported_features(
self._supported_features, self._config.get(CONF_SUPPORTED_COLOR_MODES)
)
def _set_flash_and_transition(self, message, **kwargs):
if ATTR_TRANSITION in kwargs:

View File

@ -7,6 +7,11 @@ from homeassistant.components.mqtt import valid_publish_topic, valid_subscribe_t
from homeassistant.const import (
ATTR_SERVICE_DATA,
EVENT_CALL_SERVICE,
EVENT_HOMEASSISTANT_CLOSE,
EVENT_HOMEASSISTANT_FINAL_WRITE,
EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STARTED,
EVENT_HOMEASSISTANT_STOP,
EVENT_STATE_CHANGED,
EVENT_TIME_CHANGED,
MATCH_ALL,
@ -37,6 +42,14 @@ CONFIG_SCHEMA = vol.Schema(
extra=vol.ALLOW_EXTRA,
)
BLOCKED_EVENTS = [
EVENT_HOMEASSISTANT_CLOSE,
EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STARTED,
EVENT_HOMEASSISTANT_STOP,
EVENT_HOMEASSISTANT_FINAL_WRITE,
]
async def async_setup(hass, config):
"""Set up the MQTT eventstream component."""
@ -45,16 +58,15 @@ async def async_setup(hass, config):
pub_topic = conf.get(CONF_PUBLISH_TOPIC)
sub_topic = conf.get(CONF_SUBSCRIBE_TOPIC)
ignore_event = conf.get(CONF_IGNORE_EVENT)
ignore_event.append(EVENT_TIME_CHANGED)
@callback
def _event_publisher(event):
"""Handle events by publishing them on the MQTT queue."""
if event.origin != EventOrigin.local:
return
if event.event_type == EVENT_TIME_CHANGED:
return
# User-defined events to ignore
# Events to ignore
if event.event_type in ignore_event:
return
@ -84,6 +96,10 @@ async def async_setup(hass, config):
event_type = event.get("event_type")
event_data = event.get("event_data")
# Don't fire HOMEASSISTANT_* events on this instance
if event_type in BLOCKED_EVENTS:
return
# Special case handling for event STATE_CHANGED
# We will try to convert state dicts back to State objects
# Copied over from the _handle_api_post_events_event method

View File

@ -1,6 +1,8 @@
"""Support for MySensors sensors."""
from typing import Callable
from awesomeversion import AwesomeVersion
from homeassistant.components import mysensors
from homeassistant.components.mysensors import on_unload
from homeassistant.components.mysensors.const import MYSENSORS_DISCOVERY
@ -115,7 +117,7 @@ class MySensorsSensor(mysensors.device.MySensorsEntity, SensorEntity):
"""Return the unit of measurement of this entity."""
set_req = self.gateway.const.SetReq
if (
float(self.gateway.protocol_version) >= 1.5
AwesomeVersion(self.gateway.protocol_version) >= AwesomeVersion("1.5")
and set_req.V_UNIT_PREFIX in self._values
):
return self._values[set_req.V_UNIT_PREFIX]

View File

@ -2,7 +2,7 @@
"domain": "spotify",
"name": "Spotify",
"documentation": "https://www.home-assistant.io/integrations/spotify",
"requirements": ["spotipy==2.17.1"],
"requirements": ["spotipy==2.18.0"],
"zeroconf": ["_spotify-connect._tcp.local."],
"dependencies": ["http"],
"codeowners": ["@frenck"],

View File

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

View File

@ -1144,10 +1144,7 @@ class Script:
self._log("Already running", level=LOGSEVERITY[self._max_exceeded])
script_execution_set("failed_single")
return
if self.script_mode == SCRIPT_MODE_RESTART:
self._log("Restarting")
await self.async_stop(update_state=False)
elif len(self._runs) == self.max_runs:
if self.script_mode != SCRIPT_MODE_RESTART and self.runs == self.max_runs:
if self._max_exceeded != "SILENT":
self._log(
"Maximum number of runs exceeded",
@ -1186,6 +1183,14 @@ class Script:
self._hass, self, cast(dict, variables), context, self._log_exceptions
)
self._runs.append(run)
if self.script_mode == SCRIPT_MODE_RESTART:
# When script mode is SCRIPT_MODE_RESTART, first add the new run and then
# stop any other runs. If we stop other runs first, self.is_running will
# return false after the other script runs were stopped until our task
# resumes running.
self._log("Restarting")
await self.async_stop(update_state=False, spare=run)
if started_action:
self._hass.async_run_job(started_action)
self.last_triggered = utcnow()
@ -1198,17 +1203,21 @@ class Script:
self._changed()
raise
async def _async_stop(self, update_state):
aws = [asyncio.create_task(run.async_stop()) for run in self._runs]
async def _async_stop(self, update_state, spare=None):
aws = [
asyncio.create_task(run.async_stop()) for run in self._runs if run != spare
]
if not aws:
return
await asyncio.wait(aws)
if update_state:
self._changed()
async def async_stop(self, update_state: bool = True) -> None:
async def async_stop(
self, update_state: bool = True, spare: _ScriptRun | None = None
) -> None:
"""Stop running script."""
await asyncio.shield(self._async_stop(update_state))
await asyncio.shield(self._async_stop(update_state, spare))
async def _async_get_condition(self, config):
if isinstance(config, template.Template):

View File

@ -1,6 +1,6 @@
PyJWT==1.7.1
PyNaCl==1.3.0
aiodiscover==1.3.3
aiodiscover==1.3.4
aiohttp==3.7.4.post0
aiohttp_cors==0.7.0
astral==1.10.1

View File

@ -22,6 +22,7 @@ BASE_PLATFORMS = {
"air_quality",
"alarm_control_panel",
"binary_sensor",
"camera",
"climate",
"cover",
"device_tracker",

View File

@ -147,7 +147,7 @@ aioazuredevops==1.3.5
aiobotocore==0.11.1
# homeassistant.components.dhcp
aiodiscover==1.3.3
aiodiscover==1.3.4
# homeassistant.components.dnsip
# homeassistant.components.minecraft_server
@ -2117,7 +2117,7 @@ spiderpy==1.4.2
spotcrime==1.0.4
# homeassistant.components.spotify
spotipy==2.17.1
spotipy==2.18.0
# homeassistant.components.recorder
# homeassistant.components.sql

View File

@ -84,7 +84,7 @@ aioazuredevops==1.3.5
aiobotocore==0.11.1
# homeassistant.components.dhcp
aiodiscover==1.3.3
aiodiscover==1.3.4
# homeassistant.components.dnsip
# homeassistant.components.minecraft_server
@ -1104,7 +1104,7 @@ speedtest-cli==2.1.3
spiderpy==1.4.2
# homeassistant.components.spotify
spotipy==2.17.1
spotipy==2.18.0
# homeassistant.components.recorder
# homeassistant.components.sql

View File

@ -156,8 +156,8 @@ async def test_motion_light(hass):
# Turn on motion
hass.states.async_set("binary_sensor.kitchen", "on")
# Can't block till done because delay is active
# So wait 5 event loop iterations to process script
for _ in range(5):
# So wait 10 event loop iterations to process script
for _ in range(10):
await asyncio.sleep(0)
assert len(turn_on_calls) == 1
@ -165,7 +165,7 @@ async def test_motion_light(hass):
# Test light doesn't turn off if motion stays
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=200))
for _ in range(5):
for _ in range(10):
await asyncio.sleep(0)
assert len(turn_off_calls) == 0
@ -173,7 +173,7 @@ async def test_motion_light(hass):
# Test light turns off off 120s after last motion
hass.states.async_set("binary_sensor.kitchen", "off")
for _ in range(5):
for _ in range(10):
await asyncio.sleep(0)
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=120))
@ -184,7 +184,7 @@ async def test_motion_light(hass):
# Test restarting the script
hass.states.async_set("binary_sensor.kitchen", "on")
for _ in range(5):
for _ in range(10):
await asyncio.sleep(0)
assert len(turn_on_calls) == 2
@ -192,7 +192,7 @@ async def test_motion_light(hass):
hass.states.async_set("binary_sensor.kitchen", "off")
for _ in range(5):
for _ in range(10):
await asyncio.sleep(0)
hass.states.async_set("binary_sensor.kitchen", "on")

View File

@ -1,9 +1,14 @@
"""Test the Coronavirus config flow."""
from unittest.mock import MagicMock, patch
from aiohttp import ClientError
from homeassistant import config_entries, setup
from homeassistant.components.coronavirus.const import DOMAIN, OPTION_WORLDWIDE
from homeassistant.core import HomeAssistant
async def test_form(hass):
async def test_form(hass: HomeAssistant) -> None:
"""Test we get the form."""
await setup.async_setup_component(hass, "persistent_notification", {})
result = await hass.config_entries.flow.async_init(
@ -24,3 +29,22 @@ async def test_form(hass):
}
await hass.async_block_till_done()
assert len(hass.states.async_all()) == 4
@patch(
"coronavirus.get_cases",
side_effect=ClientError,
)
async def test_abort_on_connection_error(
mock_get_cases: MagicMock, hass: HomeAssistant
) -> None:
"""Test we abort on connection error."""
await setup.async_setup_component(hass, "persistent_notification", {})
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert "type" in result
assert result["type"] == "abort"
assert "reason" in result
assert result["reason"] == "cannot_connect"

View File

@ -1,12 +1,18 @@
"""Test init of Coronavirus integration."""
from unittest.mock import MagicMock, patch
from aiohttp import ClientError
from homeassistant.components.coronavirus.const import DOMAIN, OPTION_WORLDWIDE
from homeassistant.config_entries import ENTRY_STATE_SETUP_RETRY
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry, mock_registry
async def test_migration(hass):
async def test_migration(hass: HomeAssistant) -> None:
"""Test that we can migrate coronavirus to stable unique ID."""
nl_entry = MockConfigEntry(domain=DOMAIN, title="Netherlands", data={"country": 34})
nl_entry.add_to_hass(hass)
@ -47,3 +53,20 @@ async def test_migration(hass):
assert nl_entry.unique_id == "Netherlands"
assert worldwide_entry.unique_id == OPTION_WORLDWIDE
@patch(
"coronavirus.get_cases",
side_effect=ClientError,
)
async def test_config_entry_not_ready(
mock_get_cases: MagicMock, hass: HomeAssistant
) -> None:
"""Test the configuration entry not ready."""
entry = MockConfigEntry(domain=DOMAIN, title="Netherlands", data={"country": 34})
entry.add_to_hass(hass)
assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
assert entry.state == ENTRY_STATE_SETUP_RETRY

View File

@ -145,6 +145,8 @@ async def test_setup_creates_entries_for_accessory_mode_devices(hass):
hass.states.async_set("camera.one", "on")
hass.states.async_set("camera.existing", "on")
hass.states.async_set("media_player.two", "on", {"device_class": "tv"})
hass.states.async_set("remote.standard", "on")
hass.states.async_set("remote.activity", "on", {"supported_features": 4})
bridge_mode_entry = MockConfigEntry(
domain=DOMAIN,
@ -178,7 +180,7 @@ async def test_setup_creates_entries_for_accessory_mode_devices(hass):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{"include_domains": ["camera", "media_player", "light"]},
{"include_domains": ["camera", "media_player", "light", "remote"]},
)
assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result2["step_id"] == "pairing"
@ -205,7 +207,7 @@ async def test_setup_creates_entries_for_accessory_mode_devices(hass):
"filter": {
"exclude_domains": [],
"exclude_entities": [],
"include_domains": ["media_player", "light"],
"include_domains": ["media_player", "light", "remote"],
"include_entities": [],
},
"exclude_accessory_mode": True,
@ -222,7 +224,8 @@ async def test_setup_creates_entries_for_accessory_mode_devices(hass):
# 3 - new bridge
# 4 - camera.one in accessory mode
# 5 - media_player.two in accessory mode
assert len(mock_setup_entry.mock_calls) == 5
# 6 - remote.activity in accessory mode
assert len(mock_setup_entry.mock_calls) == 6
async def test_import(hass):

View File

@ -234,10 +234,10 @@ async def test_rgb_light(hass, mqtt_mock):
state = hass.states.get("light.test")
expected_features = (
light.SUPPORT_TRANSITION
light.SUPPORT_BRIGHTNESS
| light.SUPPORT_COLOR
| light.SUPPORT_FLASH
| light.SUPPORT_BRIGHTNESS
| light.SUPPORT_TRANSITION
)
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features
@ -261,7 +261,8 @@ async def test_no_color_brightness_color_temp_white_val_if_no_topics(hass, mqtt_
state = hass.states.get("light.test")
assert state.state == STATE_OFF
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == 40
expected_features = light.SUPPORT_FLASH | light.SUPPORT_TRANSITION
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features
assert state.attributes.get("rgb_color") is None
assert state.attributes.get("brightness") is None
assert state.attributes.get("color_temp") is None
@ -310,7 +311,16 @@ async def test_controlling_state_via_topic(hass, mqtt_mock):
state = hass.states.get("light.test")
assert state.state == STATE_OFF
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == 191
expected_features = (
light.SUPPORT_BRIGHTNESS
| light.SUPPORT_COLOR
| light.SUPPORT_COLOR_TEMP
| light.SUPPORT_EFFECT
| light.SUPPORT_FLASH
| light.SUPPORT_TRANSITION
| light.SUPPORT_WHITE_VALUE
)
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features
assert state.attributes.get("rgb_color") is None
assert state.attributes.get("brightness") is None
assert state.attributes.get("color_temp") is None
@ -429,7 +439,15 @@ async def test_controlling_state_via_topic2(hass, mqtt_mock, caplog):
state = hass.states.get("light.test")
assert state.state == STATE_OFF
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == 44
expected_features = (
light.SUPPORT_BRIGHTNESS
| light.SUPPORT_COLOR
| light.SUPPORT_COLOR_TEMP
| light.SUPPORT_EFFECT
| light.SUPPORT_FLASH
| light.SUPPORT_TRANSITION
)
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features
assert state.attributes.get("brightness") is None
assert state.attributes.get("color_mode") is None
assert state.attributes.get("color_temp") is None
@ -610,7 +628,16 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock):
assert state.attributes.get("effect") == "random"
assert state.attributes.get("color_temp") == 100
assert state.attributes.get("white_value") == 50
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == 191
expected_features = (
light.SUPPORT_BRIGHTNESS
| light.SUPPORT_COLOR
| light.SUPPORT_COLOR_TEMP
| light.SUPPORT_EFFECT
| light.SUPPORT_FLASH
| light.SUPPORT_TRANSITION
| light.SUPPORT_WHITE_VALUE
)
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features
assert state.attributes.get(ATTR_ASSUMED_STATE)
await common.async_turn_on(hass, "light.test")
@ -738,7 +765,15 @@ async def test_sending_mqtt_commands_and_optimistic2(hass, mqtt_mock):
state = hass.states.get("light.test")
assert state.state == STATE_ON
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == 44
expected_features = (
light.SUPPORT_BRIGHTNESS
| light.SUPPORT_COLOR
| light.SUPPORT_COLOR_TEMP
| light.SUPPORT_EFFECT
| light.SUPPORT_FLASH
| light.SUPPORT_TRANSITION
)
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features
assert state.attributes.get("brightness") == 95
assert state.attributes.get("color_mode") == "rgb"
assert state.attributes.get("color_temp") is None
@ -1313,7 +1348,10 @@ async def test_effect(hass, mqtt_mock):
state = hass.states.get("light.test")
assert state.state == STATE_OFF
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == 44
expected_features = (
light.SUPPORT_EFFECT | light.SUPPORT_FLASH | light.SUPPORT_TRANSITION
)
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features
await common.async_turn_on(hass, "light.test")
@ -1373,7 +1411,8 @@ async def test_flash_short_and_long(hass, mqtt_mock):
state = hass.states.get("light.test")
assert state.state == STATE_OFF
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == 40
expected_features = light.SUPPORT_FLASH | light.SUPPORT_TRANSITION
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features
await common.async_turn_on(hass, "light.test", flash="short")
@ -1431,8 +1470,8 @@ async def test_transition(hass, mqtt_mock):
state = hass.states.get("light.test")
assert state.state == STATE_OFF
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == 40
expected_features = light.SUPPORT_FLASH | light.SUPPORT_TRANSITION
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features
await common.async_turn_on(hass, "light.test", transition=15)
mqtt_mock.async_publish.assert_called_once_with(
@ -1523,7 +1562,15 @@ async def test_invalid_values(hass, mqtt_mock):
state = hass.states.get("light.test")
assert state.state == STATE_OFF
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == 187
expected_features = (
light.SUPPORT_BRIGHTNESS
| light.SUPPORT_COLOR
| light.SUPPORT_COLOR_TEMP
| light.SUPPORT_FLASH
| light.SUPPORT_TRANSITION
| light.SUPPORT_WHITE_VALUE
)
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == expected_features
assert state.attributes.get("rgb_color") is None
assert state.attributes.get("brightness") is None
assert state.attributes.get("white_value") is None

View File

@ -3,7 +3,7 @@ import json
from unittest.mock import ANY, patch
import homeassistant.components.mqtt_eventstream as eventstream
from homeassistant.const import EVENT_STATE_CHANGED
from homeassistant.const import EVENT_STATE_CHANGED, MATCH_ALL
from homeassistant.core import State, callback
from homeassistant.helpers.json import JSONEncoder
from homeassistant.setup import async_setup_component
@ -114,6 +114,7 @@ async def test_time_event_does_not_send_message(hass, mqtt_mock):
mqtt_mock.async_publish.reset_mock()
async_fire_time_changed(hass, dt_util.utcnow())
await hass.async_block_till_done()
assert not mqtt_mock.async_publish.called
@ -140,6 +141,33 @@ async def test_receiving_remote_event_fires_hass_event(hass, mqtt_mock):
assert len(calls) == 1
await hass.async_block_till_done()
async def test_receiving_blocked_event_fires_hass_event(hass, mqtt_mock):
"""Test the receiving of blocked event does not fire."""
sub_topic = "foo"
assert await add_eventstream(hass, sub_topic=sub_topic)
await hass.async_block_till_done()
calls = []
@callback
def listener(_):
calls.append(1)
hass.bus.async_listen(MATCH_ALL, listener)
await hass.async_block_till_done()
for event in eventstream.BLOCKED_EVENTS:
payload = json.dumps({"event_type": event, "event_data": {}}, cls=JSONEncoder)
async_fire_mqtt_message(hass, sub_topic, payload)
await hass.async_block_till_done()
assert len(calls) == 0
await hass.async_block_till_done()
async def test_ignored_event_doesnt_send_over_stream(hass, mqtt_mock):
"""Test the ignoring of sending events if defined."""
@ -159,6 +187,7 @@ async def test_ignored_event_doesnt_send_over_stream(hass, mqtt_mock):
# Set a state of an entity
mock_state_change_event(hass, State(e_id, "on"))
await hass.async_block_till_done()
await hass.async_block_till_done()
assert not mqtt_mock.async_publish.called