mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 15:47:12 +00:00
Merge pull request #49139 from home-assistant/rc
This commit is contained in:
commit
b5548c57fb
@ -133,7 +133,7 @@ class CastOptionsFlowHandler(config_entries.OptionsFlow):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not bad_cec and not bad_hosts and not bad_uuid:
|
if not bad_cec and not bad_hosts and not bad_uuid:
|
||||||
updated_config = {}
|
updated_config = dict(current_config)
|
||||||
updated_config[CONF_IGNORE_CEC] = ignore_cec
|
updated_config[CONF_IGNORE_CEC] = ignore_cec
|
||||||
updated_config[CONF_KNOWN_HOSTS] = known_hosts
|
updated_config[CONF_KNOWN_HOSTS] = known_hosts
|
||||||
updated_config[CONF_UUID] = wanted_uuid
|
updated_config[CONF_UUID] = wanted_uuid
|
||||||
|
@ -7,6 +7,7 @@ from datetime import timedelta
|
|||||||
import functools as ft
|
import functools as ft
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
from urllib.parse import quote
|
||||||
|
|
||||||
import pychromecast
|
import pychromecast
|
||||||
from pychromecast.controllers.homeassistant import HomeAssistantController
|
from pychromecast.controllers.homeassistant import HomeAssistantController
|
||||||
@ -472,7 +473,7 @@ class CastDevice(MediaPlayerEntity):
|
|||||||
media_id = async_sign_path(
|
media_id = async_sign_path(
|
||||||
self.hass,
|
self.hass,
|
||||||
refresh_token.id,
|
refresh_token.id,
|
||||||
media_id,
|
quote(media_id),
|
||||||
timedelta(seconds=media_source.DEFAULT_EXPIRY_TIME),
|
timedelta(seconds=media_source.DEFAULT_EXPIRY_TIME),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -209,8 +209,11 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow):
|
|||||||
}
|
}
|
||||||
|
|
||||||
if "id" not in properties:
|
if "id" not in properties:
|
||||||
_LOGGER.warning(
|
# This can happen if the TXT record is received after the PTR record
|
||||||
"HomeKit device %s: id not exposed, in violation of spec", properties
|
# we will wait for the next update in this case
|
||||||
|
_LOGGER.debug(
|
||||||
|
"HomeKit device %s: id not exposed; TXT record may have not yet been received",
|
||||||
|
properties,
|
||||||
)
|
)
|
||||||
return self.async_abort(reason="invalid_properties")
|
return self.async_abort(reason="invalid_properties")
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
|
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"aiohomekit==0.2.60"
|
"aiohomekit==0.2.61"
|
||||||
],
|
],
|
||||||
"zeroconf": [
|
"zeroconf": [
|
||||||
"_hap._tcp.local."
|
"_hap._tcp.local."
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
"""Authentication for HTTP component."""
|
"""Authentication for HTTP component."""
|
||||||
import logging
|
import logging
|
||||||
import secrets
|
import secrets
|
||||||
|
from urllib.parse import unquote
|
||||||
|
|
||||||
from aiohttp import hdrs
|
from aiohttp import hdrs
|
||||||
from aiohttp.web import middleware
|
from aiohttp.web import middleware
|
||||||
@ -30,11 +31,16 @@ def async_sign_path(hass, refresh_token_id, path, expiration):
|
|||||||
|
|
||||||
now = dt_util.utcnow()
|
now = dt_util.utcnow()
|
||||||
encoded = jwt.encode(
|
encoded = jwt.encode(
|
||||||
{"iss": refresh_token_id, "path": path, "iat": now, "exp": now + expiration},
|
{
|
||||||
|
"iss": refresh_token_id,
|
||||||
|
"path": unquote(path),
|
||||||
|
"iat": now,
|
||||||
|
"exp": now + expiration,
|
||||||
|
},
|
||||||
secret,
|
secret,
|
||||||
algorithm="HS256",
|
algorithm="HS256",
|
||||||
)
|
)
|
||||||
return f"{path}?{SIGN_QUERY_PARAM}=" f"{encoded.decode()}"
|
return f"{path}?{SIGN_QUERY_PARAM}={encoded.decode()}"
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
@ -114,7 +114,7 @@ def _add_log_filter(logger, patterns):
|
|||||||
"""Add a Filter to the logger based on a regexp of the filter_str."""
|
"""Add a Filter to the logger based on a regexp of the filter_str."""
|
||||||
|
|
||||||
def filter_func(logrecord):
|
def filter_func(logrecord):
|
||||||
return not any(p.match(logrecord.getMessage()) for p in patterns)
|
return not any(p.search(logrecord.getMessage()) for p in patterns)
|
||||||
|
|
||||||
logger.addFilter(filter_func)
|
logger.addFilter(filter_func)
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from time import gmtime, strftime, time
|
from time import localtime, strftime, time
|
||||||
|
|
||||||
from aiolyric.objects.device import LyricDevice
|
from aiolyric.objects.device import LyricDevice
|
||||||
from aiolyric.objects.location import LyricLocation
|
from aiolyric.objects.location import LyricLocation
|
||||||
@ -82,7 +82,7 @@ SCHEMA_HOLD_TIME = {
|
|||||||
vol.Required(ATTR_TIME_PERIOD, default="01:00:00"): vol.All(
|
vol.Required(ATTR_TIME_PERIOD, default="01:00:00"): vol.All(
|
||||||
cv.time_period,
|
cv.time_period,
|
||||||
cv.positive_timedelta,
|
cv.positive_timedelta,
|
||||||
lambda td: strftime("%H:%M:%S", gmtime(time() + td.total_seconds())),
|
lambda td: strftime("%H:%M:%S", localtime(time() + td.total_seconds())),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import voluptuous as vol
|
|||||||
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_SCAN_INTERVAL
|
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_SCAN_INTERVAL
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.discovery import load_platform
|
from homeassistant.helpers.discovery import load_platform
|
||||||
|
from homeassistant.util.dt import now
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -59,7 +60,7 @@ def setup(hass, config):
|
|||||||
scan_interval = gateway[CONF_SCAN_INTERVAL].total_seconds()
|
scan_interval = gateway[CONF_SCAN_INTERVAL].total_seconds()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cube = MaxCube(host, port)
|
cube = MaxCube(host, port, now=now)
|
||||||
hass.data[DATA_KEY][host] = MaxCubeHandle(cube, scan_interval)
|
hass.data[DATA_KEY][host] = MaxCubeHandle(cube, scan_interval)
|
||||||
except timeout as ex:
|
except timeout as ex:
|
||||||
_LOGGER.error("Unable to connect to Max!Cube gateway: %s", str(ex))
|
_LOGGER.error("Unable to connect to Max!Cube gateway: %s", str(ex))
|
||||||
|
@ -2,6 +2,6 @@
|
|||||||
"domain": "maxcube",
|
"domain": "maxcube",
|
||||||
"name": "eQ-3 MAX!",
|
"name": "eQ-3 MAX!",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/maxcube",
|
"documentation": "https://www.home-assistant.io/integrations/maxcube",
|
||||||
"requirements": ["maxcube-api==0.4.1"],
|
"requirements": ["maxcube-api==0.4.2"],
|
||||||
"codeowners": []
|
"codeowners": []
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
from urllib.parse import quote
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
@ -123,7 +124,7 @@ async def websocket_resolve_media(hass, connection, msg):
|
|||||||
url = async_sign_path(
|
url = async_sign_path(
|
||||||
hass,
|
hass,
|
||||||
connection.refresh_token_id,
|
connection.refresh_token_id,
|
||||||
url,
|
quote(url),
|
||||||
timedelta(seconds=msg["expires"]),
|
timedelta(seconds=msg["expires"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
"""Support for MQTT fans."""
|
"""Support for MQTT fans."""
|
||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
|
import math
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
@ -441,13 +442,12 @@ class MqttFan(MqttEntity, FanEntity):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not self._feature_percentage:
|
if speed in self._legacy_speeds_list_no_off:
|
||||||
if speed in self._legacy_speeds_list_no_off:
|
self._percentage = ordered_list_item_to_percentage(
|
||||||
self._percentage = ordered_list_item_to_percentage(
|
self._legacy_speeds_list_no_off, speed
|
||||||
self._legacy_speeds_list_no_off, speed
|
)
|
||||||
)
|
elif speed == SPEED_OFF:
|
||||||
elif speed == SPEED_OFF:
|
self._percentage = 0
|
||||||
self._percentage = 0
|
|
||||||
|
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@ -592,7 +592,7 @@ class MqttFan(MqttEntity, FanEntity):
|
|||||||
|
|
||||||
This method is a coroutine.
|
This method is a coroutine.
|
||||||
"""
|
"""
|
||||||
percentage_payload = int(
|
percentage_payload = math.ceil(
|
||||||
percentage_to_ranged_value(self._speed_range, percentage)
|
percentage_to_ranged_value(self._speed_range, percentage)
|
||||||
)
|
)
|
||||||
mqtt_payload = self._command_templates[ATTR_PERCENTAGE](percentage_payload)
|
mqtt_payload = self._command_templates[ATTR_PERCENTAGE](percentage_payload)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"domain": "nexia",
|
"domain": "nexia",
|
||||||
"name": "Nexia",
|
"name": "Nexia",
|
||||||
"requirements": ["nexia==0.9.5"],
|
"requirements": ["nexia==0.9.6"],
|
||||||
"codeowners": ["@bdraco"],
|
"codeowners": ["@bdraco"],
|
||||||
"documentation": "https://www.home-assistant.io/integrations/nexia",
|
"documentation": "https://www.home-assistant.io/integrations/nexia",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
|
@ -13,4 +13,6 @@ def is_invalid_auth_code(http_status_code):
|
|||||||
|
|
||||||
def percent_conv(val):
|
def percent_conv(val):
|
||||||
"""Convert an actual percentage (0.0-1.0) to 0-100 scale."""
|
"""Convert an actual percentage (0.0-1.0) to 0-100 scale."""
|
||||||
|
if val is None:
|
||||||
|
return None
|
||||||
return round(val * 100.0, 1)
|
return round(val * 100.0, 1)
|
||||||
|
@ -134,8 +134,12 @@ class PhilipsTVDataUpdateCoordinator(DataUpdateCoordinator[None]):
|
|||||||
|
|
||||||
async def _notify_task(self):
|
async def _notify_task(self):
|
||||||
while self.api.on and self.api.notify_change_supported:
|
while self.api.on and self.api.notify_change_supported:
|
||||||
if await self.api.notifyChange(130):
|
res = await self.api.notifyChange(130)
|
||||||
|
if res:
|
||||||
self.async_set_updated_data(None)
|
self.async_set_updated_data(None)
|
||||||
|
elif res is None:
|
||||||
|
LOGGER.debug("Aborting notify due to unexpected return")
|
||||||
|
break
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_notify_stop(self):
|
def _async_notify_stop(self):
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "Philips TV",
|
"name": "Philips TV",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/philips_js",
|
"documentation": "https://www.home-assistant.io/integrations/philips_js",
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"ha-philipsjs==2.3.2"
|
"ha-philipsjs==2.7.0"
|
||||||
],
|
],
|
||||||
"codeowners": [
|
"codeowners": [
|
||||||
"@elupus"
|
"@elupus"
|
||||||
|
@ -47,10 +47,12 @@ async def async_setup(hass: HomeAssistantType, config: dict) -> bool:
|
|||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool:
|
||||||
"""Set up Roku from a config entry."""
|
"""Set up Roku from a config entry."""
|
||||||
coordinator = RokuDataUpdateCoordinator(hass, host=entry.data[CONF_HOST])
|
coordinator = hass.data[DOMAIN].get(entry.entry_id)
|
||||||
await coordinator.async_config_entry_first_refresh()
|
if not coordinator:
|
||||||
|
coordinator = RokuDataUpdateCoordinator(hass, host=entry.data[CONF_HOST])
|
||||||
|
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||||
|
|
||||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
for platform in PLATFORMS:
|
for platform in PLATFORMS:
|
||||||
hass.async_create_task(
|
hass.async_create_task(
|
||||||
|
@ -195,9 +195,15 @@ class ScreenlogicEntity(CoordinatorEntity):
|
|||||||
"""Return device information for the controller."""
|
"""Return device information for the controller."""
|
||||||
controller_type = self.config_data["controller_type"]
|
controller_type = self.config_data["controller_type"]
|
||||||
hardware_type = self.config_data["hardware_type"]
|
hardware_type = self.config_data["hardware_type"]
|
||||||
|
try:
|
||||||
|
equipment_model = EQUIPMENT.CONTROLLER_HARDWARE[controller_type][
|
||||||
|
hardware_type
|
||||||
|
]
|
||||||
|
except KeyError:
|
||||||
|
equipment_model = f"Unknown Model C:{controller_type} H:{hardware_type}"
|
||||||
return {
|
return {
|
||||||
"connections": {(dr.CONNECTION_NETWORK_MAC, self.mac)},
|
"connections": {(dr.CONNECTION_NETWORK_MAC, self.mac)},
|
||||||
"name": self.gateway_name,
|
"name": self.gateway_name,
|
||||||
"manufacturer": "Pentair",
|
"manufacturer": "Pentair",
|
||||||
"model": EQUIPMENT.CONTROLLER_HARDWARE[controller_type][hardware_type],
|
"model": equipment_model,
|
||||||
}
|
}
|
||||||
|
@ -118,15 +118,16 @@ class ShellyLight(ShellyBlockEntity, LightEntity):
|
|||||||
"""Brightness of light."""
|
"""Brightness of light."""
|
||||||
if self.mode == "color":
|
if self.mode == "color":
|
||||||
if self.control_result:
|
if self.control_result:
|
||||||
brightness = self.control_result["gain"]
|
brightness_pct = self.control_result["gain"]
|
||||||
else:
|
else:
|
||||||
brightness = self.block.gain
|
brightness_pct = self.block.gain
|
||||||
else:
|
else:
|
||||||
if self.control_result:
|
if self.control_result:
|
||||||
brightness = self.control_result["brightness"]
|
brightness_pct = self.control_result["brightness"]
|
||||||
else:
|
else:
|
||||||
brightness = self.block.brightness
|
brightness_pct = self.block.brightness
|
||||||
return int(brightness / 100 * 255)
|
|
||||||
|
return round(255 * brightness_pct / 100)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def white_value(self) -> int:
|
def white_value(self) -> int:
|
||||||
@ -188,11 +189,11 @@ class ShellyLight(ShellyBlockEntity, LightEntity):
|
|||||||
set_mode = None
|
set_mode = None
|
||||||
params = {"turn": "on"}
|
params = {"turn": "on"}
|
||||||
if ATTR_BRIGHTNESS in kwargs:
|
if ATTR_BRIGHTNESS in kwargs:
|
||||||
tmp_brightness = int(kwargs[ATTR_BRIGHTNESS] / 255 * 100)
|
brightness_pct = int(100 * (kwargs[ATTR_BRIGHTNESS] + 1) / 255)
|
||||||
if hasattr(self.block, "gain"):
|
if hasattr(self.block, "gain"):
|
||||||
params["gain"] = tmp_brightness
|
params["gain"] = brightness_pct
|
||||||
if hasattr(self.block, "brightness"):
|
if hasattr(self.block, "brightness"):
|
||||||
params["brightness"] = tmp_brightness
|
params["brightness"] = brightness_pct
|
||||||
if ATTR_COLOR_TEMP in kwargs:
|
if ATTR_COLOR_TEMP in kwargs:
|
||||||
color_temp = color_temperature_mired_to_kelvin(kwargs[ATTR_COLOR_TEMP])
|
color_temp = color_temperature_mired_to_kelvin(kwargs[ATTR_COLOR_TEMP])
|
||||||
color_temp = min(self._max_kelvin, max(self._min_kelvin, color_temp))
|
color_temp = min(self._max_kelvin, max(self._min_kelvin, color_temp))
|
||||||
|
@ -150,6 +150,7 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity):
|
|||||||
THERMOSTAT_OPERATING_STATE_PROPERTY,
|
THERMOSTAT_OPERATING_STATE_PROPERTY,
|
||||||
command_class=CommandClass.THERMOSTAT_OPERATING_STATE,
|
command_class=CommandClass.THERMOSTAT_OPERATING_STATE,
|
||||||
add_to_watched_value_ids=True,
|
add_to_watched_value_ids=True,
|
||||||
|
check_all_endpoints=True,
|
||||||
)
|
)
|
||||||
self._current_temp = self.get_zwave_value(
|
self._current_temp = self.get_zwave_value(
|
||||||
THERMOSTAT_CURRENT_TEMP_PROPERTY,
|
THERMOSTAT_CURRENT_TEMP_PROPERTY,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""Constants used by Home Assistant components."""
|
"""Constants used by Home Assistant components."""
|
||||||
MAJOR_VERSION = 2021
|
MAJOR_VERSION = 2021
|
||||||
MINOR_VERSION = 4
|
MINOR_VERSION = 4
|
||||||
PATCH_VERSION = "3"
|
PATCH_VERSION = "4"
|
||||||
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||||
__version__ = f"{__short_version__}.{PATCH_VERSION}"
|
__version__ = f"{__short_version__}.{PATCH_VERSION}"
|
||||||
REQUIRED_PYTHON_VER = (3, 8, 0)
|
REQUIRED_PYTHON_VER = (3, 8, 0)
|
||||||
|
@ -172,7 +172,7 @@ aioguardian==1.0.4
|
|||||||
aioharmony==0.2.7
|
aioharmony==0.2.7
|
||||||
|
|
||||||
# homeassistant.components.homekit_controller
|
# homeassistant.components.homekit_controller
|
||||||
aiohomekit==0.2.60
|
aiohomekit==0.2.61
|
||||||
|
|
||||||
# homeassistant.components.emulated_hue
|
# homeassistant.components.emulated_hue
|
||||||
# homeassistant.components.http
|
# homeassistant.components.http
|
||||||
@ -721,7 +721,7 @@ guppy3==3.1.0
|
|||||||
ha-ffmpeg==3.0.2
|
ha-ffmpeg==3.0.2
|
||||||
|
|
||||||
# homeassistant.components.philips_js
|
# homeassistant.components.philips_js
|
||||||
ha-philipsjs==2.3.2
|
ha-philipsjs==2.7.0
|
||||||
|
|
||||||
# homeassistant.components.habitica
|
# homeassistant.components.habitica
|
||||||
habitipy==0.2.0
|
habitipy==0.2.0
|
||||||
@ -916,7 +916,7 @@ magicseaweed==1.0.3
|
|||||||
matrix-client==0.3.2
|
matrix-client==0.3.2
|
||||||
|
|
||||||
# homeassistant.components.maxcube
|
# homeassistant.components.maxcube
|
||||||
maxcube-api==0.4.1
|
maxcube-api==0.4.2
|
||||||
|
|
||||||
# homeassistant.components.mythicbeastsdns
|
# homeassistant.components.mythicbeastsdns
|
||||||
mbddns==0.1.2
|
mbddns==0.1.2
|
||||||
@ -986,7 +986,7 @@ netdisco==2.8.2
|
|||||||
neurio==0.3.1
|
neurio==0.3.1
|
||||||
|
|
||||||
# homeassistant.components.nexia
|
# homeassistant.components.nexia
|
||||||
nexia==0.9.5
|
nexia==0.9.6
|
||||||
|
|
||||||
# homeassistant.components.nextcloud
|
# homeassistant.components.nextcloud
|
||||||
nextcloudmonitor==1.1.0
|
nextcloudmonitor==1.1.0
|
||||||
|
@ -106,7 +106,7 @@ aioguardian==1.0.4
|
|||||||
aioharmony==0.2.7
|
aioharmony==0.2.7
|
||||||
|
|
||||||
# homeassistant.components.homekit_controller
|
# homeassistant.components.homekit_controller
|
||||||
aiohomekit==0.2.60
|
aiohomekit==0.2.61
|
||||||
|
|
||||||
# homeassistant.components.emulated_hue
|
# homeassistant.components.emulated_hue
|
||||||
# homeassistant.components.http
|
# homeassistant.components.http
|
||||||
@ -382,7 +382,7 @@ guppy3==3.1.0
|
|||||||
ha-ffmpeg==3.0.2
|
ha-ffmpeg==3.0.2
|
||||||
|
|
||||||
# homeassistant.components.philips_js
|
# homeassistant.components.philips_js
|
||||||
ha-philipsjs==2.3.2
|
ha-philipsjs==2.7.0
|
||||||
|
|
||||||
# homeassistant.components.habitica
|
# homeassistant.components.habitica
|
||||||
habitipy==0.2.0
|
habitipy==0.2.0
|
||||||
@ -476,7 +476,7 @@ logi_circle==0.2.2
|
|||||||
luftdaten==0.6.4
|
luftdaten==0.6.4
|
||||||
|
|
||||||
# homeassistant.components.maxcube
|
# homeassistant.components.maxcube
|
||||||
maxcube-api==0.4.1
|
maxcube-api==0.4.2
|
||||||
|
|
||||||
# homeassistant.components.mythicbeastsdns
|
# homeassistant.components.mythicbeastsdns
|
||||||
mbddns==0.1.2
|
mbddns==0.1.2
|
||||||
@ -516,7 +516,7 @@ nessclient==0.9.15
|
|||||||
netdisco==2.8.2
|
netdisco==2.8.2
|
||||||
|
|
||||||
# homeassistant.components.nexia
|
# homeassistant.components.nexia
|
||||||
nexia==0.9.5
|
nexia==0.9.6
|
||||||
|
|
||||||
# homeassistant.components.notify_events
|
# homeassistant.components.notify_events
|
||||||
notify-events==1.0.4
|
notify-events==1.0.4
|
||||||
|
244
tests/components/cast/test_config_flow.py
Normal file
244
tests/components/cast/test_config_flow.py
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
"""Tests for the Cast config flow."""
|
||||||
|
from unittest.mock import ANY, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant import config_entries, data_entry_flow
|
||||||
|
from homeassistant.components import cast
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
async def test_creating_entry_sets_up_media_player(hass):
|
||||||
|
"""Test setting up Cast loads the media player."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.cast.media_player.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
) as mock_setup, patch(
|
||||||
|
"pychromecast.discovery.discover_chromecasts", return_value=(True, None)
|
||||||
|
), patch(
|
||||||
|
"pychromecast.discovery.stop_discovery"
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
cast.DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Confirmation form
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(mock_setup.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("source", ["import", "user", "zeroconf"])
|
||||||
|
async def test_single_instance(hass, source):
|
||||||
|
"""Test we only allow a single config flow."""
|
||||||
|
MockConfigEntry(domain="cast").add_to_hass(hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
"cast", context={"source": source}
|
||||||
|
)
|
||||||
|
assert result["type"] == "abort"
|
||||||
|
assert result["reason"] == "single_instance_allowed"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_user_setup(hass):
|
||||||
|
"""Test we can finish a config flow."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
"cast", context={"source": "user"}
|
||||||
|
)
|
||||||
|
assert result["type"] == "form"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||||
|
|
||||||
|
users = await hass.auth.async_get_users()
|
||||||
|
assert len(users) == 1
|
||||||
|
assert result["type"] == "create_entry"
|
||||||
|
assert result["result"].data == {
|
||||||
|
"ignore_cec": [],
|
||||||
|
"known_hosts": [],
|
||||||
|
"uuid": [],
|
||||||
|
"user_id": users[0].id, # Home Assistant cast user
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_user_setup_options(hass):
|
||||||
|
"""Test we can finish a config flow."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
"cast", context={"source": "user"}
|
||||||
|
)
|
||||||
|
assert result["type"] == "form"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"known_hosts": "192.168.0.1, , 192.168.0.2 "}
|
||||||
|
)
|
||||||
|
|
||||||
|
users = await hass.auth.async_get_users()
|
||||||
|
assert len(users) == 1
|
||||||
|
assert result["type"] == "create_entry"
|
||||||
|
assert result["result"].data == {
|
||||||
|
"ignore_cec": [],
|
||||||
|
"known_hosts": ["192.168.0.1", "192.168.0.2"],
|
||||||
|
"uuid": [],
|
||||||
|
"user_id": users[0].id, # Home Assistant cast user
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_zeroconf_setup(hass):
|
||||||
|
"""Test we can finish a config flow through zeroconf."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
"cast", context={"source": "zeroconf"}
|
||||||
|
)
|
||||||
|
assert result["type"] == "form"
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||||
|
|
||||||
|
users = await hass.auth.async_get_users()
|
||||||
|
assert len(users) == 1
|
||||||
|
assert result["type"] == "create_entry"
|
||||||
|
assert result["result"].data == {
|
||||||
|
"ignore_cec": [],
|
||||||
|
"known_hosts": [],
|
||||||
|
"uuid": [],
|
||||||
|
"user_id": users[0].id, # Home Assistant cast user
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_suggested(schema, key):
|
||||||
|
"""Get suggested value for key in voluptuous schema."""
|
||||||
|
for k in schema.keys():
|
||||||
|
if k == key:
|
||||||
|
if k.description is None or "suggested_value" not in k.description:
|
||||||
|
return None
|
||||||
|
return k.description["suggested_value"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"parameter_data",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"known_hosts",
|
||||||
|
["192.168.0.10", "192.168.0.11"],
|
||||||
|
"192.168.0.10,192.168.0.11",
|
||||||
|
"192.168.0.1, , 192.168.0.2 ",
|
||||||
|
["192.168.0.1", "192.168.0.2"],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"uuid",
|
||||||
|
["bla", "blu"],
|
||||||
|
"bla,blu",
|
||||||
|
"foo, , bar ",
|
||||||
|
["foo", "bar"],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"ignore_cec",
|
||||||
|
["cast1", "cast2"],
|
||||||
|
"cast1,cast2",
|
||||||
|
"other_cast, , some_cast ",
|
||||||
|
["other_cast", "some_cast"],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_option_flow(hass, parameter_data):
|
||||||
|
"""Test config flow options."""
|
||||||
|
all_parameters = ["ignore_cec", "known_hosts", "uuid"]
|
||||||
|
parameter, initial, suggested, user_input, updated = parameter_data
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"ignore_cec": [],
|
||||||
|
"known_hosts": [],
|
||||||
|
"uuid": [],
|
||||||
|
}
|
||||||
|
data[parameter] = initial
|
||||||
|
config_entry = MockConfigEntry(domain="cast", data=data)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# Test ignore_cec and uuid options are hidden if advanced options are disabled
|
||||||
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "options"
|
||||||
|
data_schema = result["data_schema"].schema
|
||||||
|
assert set(data_schema) == {"known_hosts"}
|
||||||
|
orig_data = dict(config_entry.data)
|
||||||
|
|
||||||
|
# Reconfigure ignore_cec, known_hosts, uuid
|
||||||
|
context = {"source": "user", "show_advanced_options": True}
|
||||||
|
result = await hass.config_entries.options.async_init(
|
||||||
|
config_entry.entry_id, context=context
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "options"
|
||||||
|
data_schema = result["data_schema"].schema
|
||||||
|
for other_param in all_parameters:
|
||||||
|
if other_param == parameter:
|
||||||
|
continue
|
||||||
|
assert get_suggested(data_schema, other_param) == ""
|
||||||
|
assert get_suggested(data_schema, parameter) == suggested
|
||||||
|
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input={parameter: user_input},
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result["data"] is None
|
||||||
|
for other_param in all_parameters:
|
||||||
|
if other_param == parameter:
|
||||||
|
continue
|
||||||
|
assert config_entry.data[other_param] == []
|
||||||
|
assert config_entry.data[parameter] == updated
|
||||||
|
|
||||||
|
# Clear known_hosts
|
||||||
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input={"known_hosts": ""},
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result["data"] is None
|
||||||
|
assert config_entry.data == {
|
||||||
|
**orig_data,
|
||||||
|
"ignore_cec": [],
|
||||||
|
"known_hosts": [],
|
||||||
|
"uuid": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_known_hosts(hass, castbrowser_mock, castbrowser_constructor_mock):
|
||||||
|
"""Test known hosts is passed to pychromecasts."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
"cast", context={"source": "user"}
|
||||||
|
)
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"known_hosts": "192.168.0.1, 192.168.0.2"}
|
||||||
|
)
|
||||||
|
assert result["type"] == "create_entry"
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
config_entry = hass.config_entries.async_entries("cast")[0]
|
||||||
|
|
||||||
|
assert castbrowser_mock.start_discovery.call_count == 1
|
||||||
|
castbrowser_constructor_mock.assert_called_once_with(
|
||||||
|
ANY, ANY, ["192.168.0.1", "192.168.0.2"]
|
||||||
|
)
|
||||||
|
castbrowser_mock.reset_mock()
|
||||||
|
castbrowser_constructor_mock.reset_mock()
|
||||||
|
|
||||||
|
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input={"known_hosts": "192.168.0.11, 192.168.0.12"},
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
castbrowser_mock.start_discovery.assert_not_called()
|
||||||
|
castbrowser_constructor_mock.assert_not_called()
|
||||||
|
castbrowser_mock.host_browser.update_hosts.assert_called_once_with(
|
||||||
|
["192.168.0.11", "192.168.0.12"]
|
||||||
|
)
|
@ -1,39 +1,9 @@
|
|||||||
"""Tests for the Cast config flow."""
|
"""Tests for the Cast integration."""
|
||||||
from unittest.mock import ANY, patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant import config_entries, data_entry_flow
|
|
||||||
from homeassistant.components import cast
|
from homeassistant.components import cast
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
|
||||||
|
|
||||||
|
|
||||||
async def test_creating_entry_sets_up_media_player(hass):
|
|
||||||
"""Test setting up Cast loads the media player."""
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.cast.media_player.async_setup_entry",
|
|
||||||
return_value=True,
|
|
||||||
) as mock_setup, patch(
|
|
||||||
"pychromecast.discovery.discover_chromecasts", return_value=(True, None)
|
|
||||||
), patch(
|
|
||||||
"pychromecast.discovery.stop_discovery"
|
|
||||||
):
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
cast.DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Confirmation form
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(mock_setup.mock_calls) == 1
|
|
||||||
|
|
||||||
|
|
||||||
async def test_import(hass, caplog):
|
async def test_import(hass, caplog):
|
||||||
"""Test that specifying config will create an entry."""
|
"""Test that specifying config will create an entry."""
|
||||||
@ -67,7 +37,7 @@ async def test_import(hass, caplog):
|
|||||||
|
|
||||||
|
|
||||||
async def test_not_configuring_cast_not_creates_entry(hass):
|
async def test_not_configuring_cast_not_creates_entry(hass):
|
||||||
"""Test that no config will not create an entry."""
|
"""Test that an empty config does not create an entry."""
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.cast.async_setup_entry", return_value=True
|
"homeassistant.components.cast.async_setup_entry", return_value=True
|
||||||
) as mock_setup:
|
) as mock_setup:
|
||||||
@ -75,207 +45,3 @@ async def test_not_configuring_cast_not_creates_entry(hass):
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert len(mock_setup.mock_calls) == 0
|
assert len(mock_setup.mock_calls) == 0
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("source", ["import", "user", "zeroconf"])
|
|
||||||
async def test_single_instance(hass, source):
|
|
||||||
"""Test we only allow a single config flow."""
|
|
||||||
MockConfigEntry(domain="cast").add_to_hass(hass)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
"cast", context={"source": source}
|
|
||||||
)
|
|
||||||
assert result["type"] == "abort"
|
|
||||||
assert result["reason"] == "single_instance_allowed"
|
|
||||||
|
|
||||||
|
|
||||||
async def test_user_setup(hass):
|
|
||||||
"""Test we can finish a config flow."""
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
"cast", context={"source": "user"}
|
|
||||||
)
|
|
||||||
assert result["type"] == "form"
|
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
|
||||||
|
|
||||||
users = await hass.auth.async_get_users()
|
|
||||||
assert len(users) == 1
|
|
||||||
assert result["type"] == "create_entry"
|
|
||||||
assert result["result"].data == {
|
|
||||||
"ignore_cec": [],
|
|
||||||
"known_hosts": [],
|
|
||||||
"uuid": [],
|
|
||||||
"user_id": users[0].id, # Home Assistant cast user
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_user_setup_options(hass):
|
|
||||||
"""Test we can finish a config flow."""
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
"cast", context={"source": "user"}
|
|
||||||
)
|
|
||||||
assert result["type"] == "form"
|
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
|
||||||
result["flow_id"], {"known_hosts": "192.168.0.1, , 192.168.0.2 "}
|
|
||||||
)
|
|
||||||
|
|
||||||
users = await hass.auth.async_get_users()
|
|
||||||
assert len(users) == 1
|
|
||||||
assert result["type"] == "create_entry"
|
|
||||||
assert result["result"].data == {
|
|
||||||
"ignore_cec": [],
|
|
||||||
"known_hosts": ["192.168.0.1", "192.168.0.2"],
|
|
||||||
"uuid": [],
|
|
||||||
"user_id": users[0].id, # Home Assistant cast user
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_zeroconf_setup(hass):
|
|
||||||
"""Test we can finish a config flow through zeroconf."""
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
"cast", context={"source": "zeroconf"}
|
|
||||||
)
|
|
||||||
assert result["type"] == "form"
|
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
|
||||||
|
|
||||||
users = await hass.auth.async_get_users()
|
|
||||||
assert len(users) == 1
|
|
||||||
assert result["type"] == "create_entry"
|
|
||||||
assert result["result"].data == {
|
|
||||||
"ignore_cec": [],
|
|
||||||
"known_hosts": [],
|
|
||||||
"uuid": [],
|
|
||||||
"user_id": users[0].id, # Home Assistant cast user
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def get_suggested(schema, key):
|
|
||||||
"""Get suggested value for key in voluptuous schema."""
|
|
||||||
for k in schema.keys():
|
|
||||||
if k == key:
|
|
||||||
if k.description is None or "suggested_value" not in k.description:
|
|
||||||
return None
|
|
||||||
return k.description["suggested_value"]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"parameter_data",
|
|
||||||
[
|
|
||||||
(
|
|
||||||
"known_hosts",
|
|
||||||
["192.168.0.10", "192.168.0.11"],
|
|
||||||
"192.168.0.10,192.168.0.11",
|
|
||||||
"192.168.0.1, , 192.168.0.2 ",
|
|
||||||
["192.168.0.1", "192.168.0.2"],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"uuid",
|
|
||||||
["bla", "blu"],
|
|
||||||
"bla,blu",
|
|
||||||
"foo, , bar ",
|
|
||||||
["foo", "bar"],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"ignore_cec",
|
|
||||||
["cast1", "cast2"],
|
|
||||||
"cast1,cast2",
|
|
||||||
"other_cast, , some_cast ",
|
|
||||||
["other_cast", "some_cast"],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
async def test_option_flow(hass, parameter_data):
|
|
||||||
"""Test config flow options."""
|
|
||||||
all_parameters = ["ignore_cec", "known_hosts", "uuid"]
|
|
||||||
parameter, initial, suggested, user_input, updated = parameter_data
|
|
||||||
|
|
||||||
data = {
|
|
||||||
"ignore_cec": [],
|
|
||||||
"known_hosts": [],
|
|
||||||
"uuid": [],
|
|
||||||
}
|
|
||||||
data[parameter] = initial
|
|
||||||
config_entry = MockConfigEntry(domain="cast", data=data)
|
|
||||||
config_entry.add_to_hass(hass)
|
|
||||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
# Test ignore_cec and uuid options are hidden if advanced options are disabled
|
|
||||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
|
||||||
assert result["step_id"] == "options"
|
|
||||||
data_schema = result["data_schema"].schema
|
|
||||||
assert set(data_schema) == {"known_hosts"}
|
|
||||||
|
|
||||||
# Reconfigure ignore_cec, known_hosts, uuid
|
|
||||||
context = {"source": "user", "show_advanced_options": True}
|
|
||||||
result = await hass.config_entries.options.async_init(
|
|
||||||
config_entry.entry_id, context=context
|
|
||||||
)
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
|
||||||
assert result["step_id"] == "options"
|
|
||||||
data_schema = result["data_schema"].schema
|
|
||||||
for other_param in all_parameters:
|
|
||||||
if other_param == parameter:
|
|
||||||
continue
|
|
||||||
assert get_suggested(data_schema, other_param) == ""
|
|
||||||
assert get_suggested(data_schema, parameter) == suggested
|
|
||||||
|
|
||||||
result = await hass.config_entries.options.async_configure(
|
|
||||||
result["flow_id"],
|
|
||||||
user_input={parameter: user_input},
|
|
||||||
)
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
|
||||||
assert result["data"] is None
|
|
||||||
for other_param in all_parameters:
|
|
||||||
if other_param == parameter:
|
|
||||||
continue
|
|
||||||
assert config_entry.data[other_param] == []
|
|
||||||
assert config_entry.data[parameter] == updated
|
|
||||||
|
|
||||||
# Clear known_hosts
|
|
||||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
|
||||||
result = await hass.config_entries.options.async_configure(
|
|
||||||
result["flow_id"],
|
|
||||||
user_input={"known_hosts": ""},
|
|
||||||
)
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
|
||||||
assert result["data"] is None
|
|
||||||
assert config_entry.data == {"ignore_cec": [], "known_hosts": [], "uuid": []}
|
|
||||||
|
|
||||||
|
|
||||||
async def test_known_hosts(hass, castbrowser_mock, castbrowser_constructor_mock):
|
|
||||||
"""Test known hosts is passed to pychromecasts."""
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
"cast", context={"source": "user"}
|
|
||||||
)
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
|
||||||
result["flow_id"], {"known_hosts": "192.168.0.1, 192.168.0.2"}
|
|
||||||
)
|
|
||||||
assert result["type"] == "create_entry"
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
config_entry = hass.config_entries.async_entries("cast")[0]
|
|
||||||
|
|
||||||
assert castbrowser_mock.start_discovery.call_count == 1
|
|
||||||
castbrowser_constructor_mock.assert_called_once_with(
|
|
||||||
ANY, ANY, ["192.168.0.1", "192.168.0.2"]
|
|
||||||
)
|
|
||||||
castbrowser_mock.reset_mock()
|
|
||||||
castbrowser_constructor_mock.reset_mock()
|
|
||||||
|
|
||||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
|
||||||
result = await hass.config_entries.options.async_configure(
|
|
||||||
result["flow_id"],
|
|
||||||
user_input={"known_hosts": "192.168.0.11, 192.168.0.12"},
|
|
||||||
)
|
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
castbrowser_mock.start_discovery.assert_not_called()
|
|
||||||
castbrowser_constructor_mock.assert_not_called()
|
|
||||||
castbrowser_mock.host_browser.update_hosts.assert_called_once_with(
|
|
||||||
["192.168.0.11", "192.168.0.12"]
|
|
||||||
)
|
|
||||||
|
@ -42,6 +42,7 @@ async def test_log_filtering(hass, caplog):
|
|||||||
"doesntmatchanything",
|
"doesntmatchanything",
|
||||||
".*shouldfilterall.*",
|
".*shouldfilterall.*",
|
||||||
"^filterthis:.*",
|
"^filterthis:.*",
|
||||||
|
"in the middle",
|
||||||
],
|
],
|
||||||
"test.other_filter": [".*otherfilterer"],
|
"test.other_filter": [".*otherfilterer"],
|
||||||
},
|
},
|
||||||
@ -62,6 +63,7 @@ async def test_log_filtering(hass, caplog):
|
|||||||
filter_logger, False, "this line containing shouldfilterall should be filtered"
|
filter_logger, False, "this line containing shouldfilterall should be filtered"
|
||||||
)
|
)
|
||||||
msg_test(filter_logger, True, "this line should not be filtered filterthis:")
|
msg_test(filter_logger, True, "this line should not be filtered filterthis:")
|
||||||
|
msg_test(filter_logger, False, "this in the middle should be filtered")
|
||||||
msg_test(filter_logger, False, "filterthis: should be filtered")
|
msg_test(filter_logger, False, "filterthis: should be filtered")
|
||||||
msg_test(filter_logger, False, "format string shouldfilter%s", "all")
|
msg_test(filter_logger, False, "format string shouldfilter%s", "all")
|
||||||
msg_test(filter_logger, True, "format string shouldfilter%s", "not")
|
msg_test(filter_logger, True, "format string shouldfilter%s", "not")
|
||||||
|
@ -10,6 +10,7 @@ import pytest
|
|||||||
|
|
||||||
from homeassistant.components.maxcube import DOMAIN
|
from homeassistant.components.maxcube import DOMAIN
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
from homeassistant.util.dt import now
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -105,5 +106,5 @@ async def cube(hass, hass_config, room, thermostat, wallthermostat, windowshutte
|
|||||||
assert await async_setup_component(hass, DOMAIN, hass_config)
|
assert await async_setup_component(hass, DOMAIN, hass_config)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
gateway = hass_config[DOMAIN]["gateways"][0]
|
gateway = hass_config[DOMAIN]["gateways"][0]
|
||||||
mock.assert_called_with(gateway["host"], gateway.get("port", 62910))
|
mock.assert_called_with(gateway["host"], gateway.get("port", 62910), now=now)
|
||||||
return cube
|
return cube
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"""Test Media Source initialization."""
|
"""Test Media Source initialization."""
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
from urllib.parse import quote
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ async def test_async_browse_media(hass):
|
|||||||
media = await media_source.async_browse_media(hass, "")
|
media = await media_source.async_browse_media(hass, "")
|
||||||
assert isinstance(media, media_source.models.BrowseMediaSource)
|
assert isinstance(media, media_source.models.BrowseMediaSource)
|
||||||
assert media.title == "media/"
|
assert media.title == "media/"
|
||||||
assert len(media.children) == 1
|
assert len(media.children) == 2
|
||||||
|
|
||||||
# Test invalid media content
|
# Test invalid media content
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
@ -133,14 +134,15 @@ async def test_websocket_browse_media(hass, hass_ws_client):
|
|||||||
assert msg["error"]["message"] == "test"
|
assert msg["error"]["message"] == "test"
|
||||||
|
|
||||||
|
|
||||||
async def test_websocket_resolve_media(hass, hass_ws_client):
|
@pytest.mark.parametrize("filename", ["test.mp3", "Epic Sax Guy 10 Hours.mp4"])
|
||||||
|
async def test_websocket_resolve_media(hass, hass_ws_client, filename):
|
||||||
"""Test browse media websocket."""
|
"""Test browse media websocket."""
|
||||||
assert await async_setup_component(hass, const.DOMAIN, {})
|
assert await async_setup_component(hass, const.DOMAIN, {})
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
client = await hass_ws_client(hass)
|
client = await hass_ws_client(hass)
|
||||||
|
|
||||||
media = media_source.models.PlayMedia("/media/local/test.mp3", "audio/mpeg")
|
media = media_source.models.PlayMedia(f"/media/local/{filename}", "audio/mpeg")
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.media_source.async_resolve_media",
|
"homeassistant.components.media_source.async_resolve_media",
|
||||||
@ -150,7 +152,7 @@ async def test_websocket_resolve_media(hass, hass_ws_client):
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"type": "media_source/resolve_media",
|
"type": "media_source/resolve_media",
|
||||||
"media_content_id": f"{const.URI_SCHEME}{const.DOMAIN}/local/test.mp3",
|
"media_content_id": f"{const.URI_SCHEME}{const.DOMAIN}/local/{filename}",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -158,7 +160,7 @@ async def test_websocket_resolve_media(hass, hass_ws_client):
|
|||||||
|
|
||||||
assert msg["success"]
|
assert msg["success"]
|
||||||
assert msg["id"] == 1
|
assert msg["id"] == 1
|
||||||
assert msg["result"]["url"].startswith(media.url)
|
assert msg["result"]["url"].startswith(quote(media.url))
|
||||||
assert msg["result"]["mime_type"] == media.mime_type
|
assert msg["result"]["mime_type"] == media.mime_type
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
|
@ -95,5 +95,8 @@ async def test_media_view(hass, hass_client):
|
|||||||
resp = await client.get("/media/local/test.mp3")
|
resp = await client.get("/media/local/test.mp3")
|
||||||
assert resp.status == 200
|
assert resp.status == 200
|
||||||
|
|
||||||
|
resp = await client.get("/media/local/Epic Sax Guy 10 Hours.mp4")
|
||||||
|
assert resp.status == 200
|
||||||
|
|
||||||
resp = await client.get("/media/recordings/test.mp3")
|
resp = await client.get("/media/recordings/test.mp3")
|
||||||
assert resp.status == 200
|
assert resp.status == 200
|
||||||
|
@ -618,7 +618,7 @@ async def test_sending_mqtt_commands_with_alternate_speed_range(hass, mqtt_mock)
|
|||||||
"percentage_state_topic": "percentage-state-topic1",
|
"percentage_state_topic": "percentage-state-topic1",
|
||||||
"percentage_command_topic": "percentage-command-topic1",
|
"percentage_command_topic": "percentage-command-topic1",
|
||||||
"speed_range_min": 1,
|
"speed_range_min": 1,
|
||||||
"speed_range_max": 100,
|
"speed_range_max": 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"platform": "mqtt",
|
"platform": "mqtt",
|
||||||
@ -651,9 +651,25 @@ async def test_sending_mqtt_commands_with_alternate_speed_range(hass, mqtt_mock)
|
|||||||
state = hass.states.get("fan.test1")
|
state = hass.states.get("fan.test1")
|
||||||
assert state.attributes.get(ATTR_ASSUMED_STATE)
|
assert state.attributes.get(ATTR_ASSUMED_STATE)
|
||||||
|
|
||||||
|
await common.async_set_percentage(hass, "fan.test1", 33)
|
||||||
|
mqtt_mock.async_publish.assert_called_once_with(
|
||||||
|
"percentage-command-topic1", "1", 0, False
|
||||||
|
)
|
||||||
|
mqtt_mock.async_publish.reset_mock()
|
||||||
|
state = hass.states.get("fan.test1")
|
||||||
|
assert state.attributes.get(ATTR_ASSUMED_STATE)
|
||||||
|
|
||||||
|
await common.async_set_percentage(hass, "fan.test1", 66)
|
||||||
|
mqtt_mock.async_publish.assert_called_once_with(
|
||||||
|
"percentage-command-topic1", "2", 0, False
|
||||||
|
)
|
||||||
|
mqtt_mock.async_publish.reset_mock()
|
||||||
|
state = hass.states.get("fan.test1")
|
||||||
|
assert state.attributes.get(ATTR_ASSUMED_STATE)
|
||||||
|
|
||||||
await common.async_set_percentage(hass, "fan.test1", 100)
|
await common.async_set_percentage(hass, "fan.test1", 100)
|
||||||
mqtt_mock.async_publish.assert_called_once_with(
|
mqtt_mock.async_publish.assert_called_once_with(
|
||||||
"percentage-command-topic1", "100", 0, False
|
"percentage-command-topic1", "3", 0, False
|
||||||
)
|
)
|
||||||
mqtt_mock.async_publish.reset_mock()
|
mqtt_mock.async_publish.reset_mock()
|
||||||
state = hass.states.get("fan.test1")
|
state = hass.states.get("fan.test1")
|
||||||
|
@ -12,6 +12,7 @@ from homeassistant.components.climate.const import (
|
|||||||
ATTR_PRESET_MODE,
|
ATTR_PRESET_MODE,
|
||||||
ATTR_TARGET_TEMP_HIGH,
|
ATTR_TARGET_TEMP_HIGH,
|
||||||
ATTR_TARGET_TEMP_LOW,
|
ATTR_TARGET_TEMP_LOW,
|
||||||
|
CURRENT_HVAC_COOL,
|
||||||
CURRENT_HVAC_IDLE,
|
CURRENT_HVAC_IDLE,
|
||||||
DOMAIN as CLIMATE_DOMAIN,
|
DOMAIN as CLIMATE_DOMAIN,
|
||||||
HVAC_MODE_COOL,
|
HVAC_MODE_COOL,
|
||||||
@ -351,6 +352,7 @@ async def test_thermostat_different_endpoints(
|
|||||||
assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 22.8
|
assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 22.8
|
||||||
assert state.attributes[ATTR_FAN_MODE] == "Auto low"
|
assert state.attributes[ATTR_FAN_MODE] == "Auto low"
|
||||||
assert state.attributes[ATTR_FAN_STATE] == "Idle / off"
|
assert state.attributes[ATTR_FAN_STATE] == "Idle / off"
|
||||||
|
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_COOL
|
||||||
|
|
||||||
|
|
||||||
async def test_setpoint_thermostat(hass, client, climate_danfoss_lc_13, integration):
|
async def test_setpoint_thermostat(hass, client, climate_danfoss_lc_13, integration):
|
||||||
|
@ -528,7 +528,7 @@ async def test_poll_value(
|
|||||||
},
|
},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
assert len(client.async_send_command.call_args_list) == 7
|
assert len(client.async_send_command.call_args_list) == 8
|
||||||
|
|
||||||
# Test polling against an invalid entity raises ValueError
|
# Test polling against an invalid entity raises ValueError
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
|
1
tests/testing_config/media/Epic Sax Guy 10 Hours.mp4
Normal file
1
tests/testing_config/media/Epic Sax Guy 10 Hours.mp4
Normal file
@ -0,0 +1 @@
|
|||||||
|
I play the sax
|
Loading…
x
Reference in New Issue
Block a user