mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 15:17:35 +00:00
Merge pull request #40571 from home-assistant/rc
This commit is contained in:
commit
9fdab64e8b
@ -2,7 +2,7 @@
|
|||||||
"domain": "accuweather",
|
"domain": "accuweather",
|
||||||
"name": "AccuWeather",
|
"name": "AccuWeather",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/accuweather/",
|
"documentation": "https://www.home-assistant.io/integrations/accuweather/",
|
||||||
"requirements": ["accuweather==0.0.10"],
|
"requirements": ["accuweather==0.0.11"],
|
||||||
"codeowners": ["@bieniu"],
|
"codeowners": ["@bieniu"],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"quality_scale": "platinum"
|
"quality_scale": "platinum"
|
||||||
|
@ -3,11 +3,11 @@
|
|||||||
"name": "Axis",
|
"name": "Axis",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/axis",
|
"documentation": "https://www.home-assistant.io/integrations/axis",
|
||||||
"requirements": ["axis==35"],
|
"requirements": ["axis==37"],
|
||||||
"zeroconf": [
|
"zeroconf": [
|
||||||
{"type":"_axis-video._tcp.local.","macaddress":"00408C*"},
|
{ "type": "_axis-video._tcp.local.", "macaddress": "00408C*" },
|
||||||
{"type":"_axis-video._tcp.local.","macaddress":"ACCC8E*"},
|
{ "type": "_axis-video._tcp.local.", "macaddress": "ACCC8E*" },
|
||||||
{"type":"_axis-video._tcp.local.","macaddress":"B8A44F*"}
|
{ "type": "_axis-video._tcp.local.", "macaddress": "B8A44F*" }
|
||||||
],
|
],
|
||||||
"after_dependencies": ["mqtt"],
|
"after_dependencies": ["mqtt"],
|
||||||
"codeowners": ["@Kane610"]
|
"codeowners": ["@Kane610"]
|
||||||
|
@ -54,7 +54,9 @@ class BOMWeather(WeatherEntity):
|
|||||||
@property
|
@property
|
||||||
def condition(self):
|
def condition(self):
|
||||||
"""Return the current condition."""
|
"""Return the current condition."""
|
||||||
return self.bom_data.get_reading("weather")
|
return self.bom_data.get_reading("weather") or self.bom_data.get_reading(
|
||||||
|
"cloud"
|
||||||
|
)
|
||||||
|
|
||||||
# Now implement the WeatherEntity interface
|
# Now implement the WeatherEntity interface
|
||||||
|
|
||||||
|
@ -48,9 +48,9 @@ class DSMRConnection:
|
|||||||
"""Test if we can validate connection with the device."""
|
"""Test if we can validate connection with the device."""
|
||||||
|
|
||||||
def update_telegram(telegram):
|
def update_telegram(telegram):
|
||||||
self._telegram = telegram
|
if obis_ref.EQUIPMENT_IDENTIFIER in telegram:
|
||||||
|
self._telegram = telegram
|
||||||
transport.close()
|
transport.close()
|
||||||
|
|
||||||
if self._host is None:
|
if self._host is None:
|
||||||
reader_factory = partial(
|
reader_factory = partial(
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"domain": "frontend",
|
"domain": "frontend",
|
||||||
"name": "Home Assistant Frontend",
|
"name": "Home Assistant Frontend",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
||||||
"requirements": ["home-assistant-frontend==20200918.0"],
|
"requirements": ["home-assistant-frontend==20200918.2"],
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"api",
|
"api",
|
||||||
"auth",
|
"auth",
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "Gogogate2 and iSmartGate",
|
"name": "Gogogate2 and iSmartGate",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/gogogate2",
|
"documentation": "https://www.home-assistant.io/integrations/gogogate2",
|
||||||
"requirements": ["gogogate2-api==2.0.2"],
|
"requirements": ["gogogate2-api==2.0.3"],
|
||||||
"codeowners": ["@vangorra"],
|
"codeowners": ["@vangorra"],
|
||||||
"homekit": {
|
"homekit": {
|
||||||
"models": [
|
"models": [
|
||||||
|
@ -39,7 +39,7 @@ from homeassistant.const import (
|
|||||||
STATE_OPEN,
|
STATE_OPEN,
|
||||||
STATE_OPENING,
|
STATE_OPENING,
|
||||||
)
|
)
|
||||||
from homeassistant.core import State
|
from homeassistant.core import CoreState, State
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.event import async_track_state_change_event
|
from homeassistant.helpers.event import async_track_state_change_event
|
||||||
|
|
||||||
@ -162,6 +162,10 @@ class CoverGroup(GroupEntity, CoverEntity):
|
|||||||
self.hass, self._entities, self._update_supported_features_event
|
self.hass, self._entities, self._update_supported_features_event
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if self.hass.state == CoreState.running:
|
||||||
|
await self.async_update()
|
||||||
|
return
|
||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -36,7 +36,7 @@ from homeassistant.const import (
|
|||||||
STATE_ON,
|
STATE_ON,
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNAVAILABLE,
|
||||||
)
|
)
|
||||||
from homeassistant.core import State
|
from homeassistant.core import CoreState, State
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.event import async_track_state_change_event
|
from homeassistant.helpers.event import async_track_state_change_event
|
||||||
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
|
||||||
@ -111,6 +111,11 @@ class LightGroup(GroupEntity, light.LightEntity):
|
|||||||
self.hass, self._entity_ids, async_state_changed_listener
|
self.hass, self._entity_ids, async_state_changed_listener
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if self.hass.state == CoreState.running:
|
||||||
|
await self.async_update()
|
||||||
|
return
|
||||||
|
|
||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -145,7 +145,7 @@ async def async_attach_trigger(
|
|||||||
else:
|
else:
|
||||||
cur_value = new_st.attributes.get(attribute)
|
cur_value = new_st.attributes.get(attribute)
|
||||||
|
|
||||||
if CONF_TO not in config:
|
if CONF_FROM in config and CONF_TO not in config:
|
||||||
return cur_value != old_value
|
return cur_value != old_value
|
||||||
|
|
||||||
return cur_value == new_value
|
return cur_value == new_value
|
||||||
|
@ -36,7 +36,7 @@ class TimePattern:
|
|||||||
if isinstance(value, str) and value.startswith("/"):
|
if isinstance(value, str) and value.startswith("/"):
|
||||||
number = int(value[1:])
|
number = int(value[1:])
|
||||||
else:
|
else:
|
||||||
number = int(value)
|
value = number = int(value)
|
||||||
|
|
||||||
if not (0 <= number <= self.maximum):
|
if not (0 <= number <= self.maximum):
|
||||||
raise vol.Invalid(f"must be a value between 0 and {self.maximum}")
|
raise vol.Invalid(f"must be a value between 0 and {self.maximum}")
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"domain": "insteon",
|
"domain": "insteon",
|
||||||
"name": "Insteon",
|
"name": "Insteon",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/insteon",
|
"documentation": "https://www.home-assistant.io/integrations/insteon",
|
||||||
"requirements": ["pyinsteon==1.0.7"],
|
"requirements": ["pyinsteon==1.0.8"],
|
||||||
"codeowners": ["@teharris1"],
|
"codeowners": ["@teharris1"],
|
||||||
"config_flow": true
|
"config_flow": true
|
||||||
}
|
}
|
@ -202,6 +202,10 @@ class KodiConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
self._ws_port = user_input.get(CONF_WS_PORT)
|
self._ws_port = user_input.get(CONF_WS_PORT)
|
||||||
|
|
||||||
|
# optional ints return 0 rather than None when empty
|
||||||
|
if self._ws_port == 0:
|
||||||
|
self._ws_port = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await validate_ws(self.hass, self._get_data())
|
await validate_ws(self.hass, self._get_data())
|
||||||
except WSCannotConnect:
|
except WSCannotConnect:
|
||||||
|
@ -94,7 +94,12 @@ class LuciDeviceScanner(DeviceScanner):
|
|||||||
|
|
||||||
last_results = []
|
last_results = []
|
||||||
for device in result:
|
for device in result:
|
||||||
if device.reachable:
|
if (
|
||||||
|
not hasattr(self.router.router.owrt_version, "release")
|
||||||
|
or not self.router.router.owrt_version.release
|
||||||
|
or self.router.router.owrt_version.release[0] < 19
|
||||||
|
or device.reachable
|
||||||
|
):
|
||||||
last_results.append(device)
|
last_results.append(device)
|
||||||
|
|
||||||
self.last_results = last_results
|
self.last_results = last_results
|
||||||
|
@ -5,6 +5,8 @@ import voluptuous as vol
|
|||||||
|
|
||||||
from homeassistant.components.weather import (
|
from homeassistant.components.weather import (
|
||||||
ATTR_FORECAST_CONDITION,
|
ATTR_FORECAST_CONDITION,
|
||||||
|
ATTR_FORECAST_TEMP,
|
||||||
|
ATTR_FORECAST_TIME,
|
||||||
ATTR_WEATHER_HUMIDITY,
|
ATTR_WEATHER_HUMIDITY,
|
||||||
ATTR_WEATHER_PRESSURE,
|
ATTR_WEATHER_PRESSURE,
|
||||||
ATTR_WEATHER_TEMPERATURE,
|
ATTR_WEATHER_TEMPERATURE,
|
||||||
@ -209,13 +211,17 @@ class MetWeather(CoordinatorEntity, WeatherEntity):
|
|||||||
met_forecast = self.coordinator.data.hourly_forecast
|
met_forecast = self.coordinator.data.hourly_forecast
|
||||||
else:
|
else:
|
||||||
met_forecast = self.coordinator.data.daily_forecast
|
met_forecast = self.coordinator.data.daily_forecast
|
||||||
|
required_keys = {ATTR_FORECAST_TEMP, ATTR_FORECAST_TIME}
|
||||||
ha_forecast = []
|
ha_forecast = []
|
||||||
for met_item in met_forecast:
|
for met_item in met_forecast:
|
||||||
|
if not set(met_item).issuperset(required_keys):
|
||||||
|
continue
|
||||||
ha_item = {
|
ha_item = {
|
||||||
k: met_item[v] for k, v in FORECAST_MAP.items() if met_item.get(v)
|
k: met_item[v] for k, v in FORECAST_MAP.items() if met_item.get(v)
|
||||||
}
|
}
|
||||||
ha_item[ATTR_FORECAST_CONDITION] = format_condition(
|
if ha_item.get(ATTR_FORECAST_CONDITION):
|
||||||
ha_item[ATTR_FORECAST_CONDITION]
|
ha_item[ATTR_FORECAST_CONDITION] = format_condition(
|
||||||
)
|
ha_item[ATTR_FORECAST_CONDITION]
|
||||||
|
)
|
||||||
ha_forecast.append(ha_item)
|
ha_forecast.append(ha_item)
|
||||||
return ha_forecast
|
return ha_forecast
|
||||||
|
@ -158,20 +158,23 @@ class ModbusCoilSwitch(ToggleEntity, RestoreEntity):
|
|||||||
"""Update the state of the switch."""
|
"""Update the state of the switch."""
|
||||||
self._is_on = self._read_coil(self._coil)
|
self._is_on = self._read_coil(self._coil)
|
||||||
|
|
||||||
def _read_coil(self, coil) -> Optional[bool]:
|
def _read_coil(self, coil) -> bool:
|
||||||
"""Read coil using the Modbus hub slave."""
|
"""Read coil using the Modbus hub slave."""
|
||||||
try:
|
try:
|
||||||
result = self._hub.read_coils(self._slave, coil, 1)
|
result = self._hub.read_coils(self._slave, coil, 1)
|
||||||
except ConnectionException:
|
except ConnectionException:
|
||||||
self._available = False
|
self._available = False
|
||||||
return
|
return False
|
||||||
|
|
||||||
if isinstance(result, (ModbusException, ExceptionResponse)):
|
if isinstance(result, (ModbusException, ExceptionResponse)):
|
||||||
self._available = False
|
self._available = False
|
||||||
return
|
return False
|
||||||
|
|
||||||
self._available = True
|
self._available = True
|
||||||
return bool(result.bits[coil])
|
# bits[0] select the lowest bit in result,
|
||||||
|
# is_on for a binary_sensor is true if the bit are 1
|
||||||
|
# The other bits are not considered.
|
||||||
|
return bool(result.bits[0])
|
||||||
|
|
||||||
def _write_coil(self, coil, value):
|
def _write_coil(self, coil, value):
|
||||||
"""Write coil using the Modbus hub slave."""
|
"""Write coil using the Modbus hub slave."""
|
||||||
|
@ -100,6 +100,7 @@ def setup(hass, config):
|
|||||||
_LOGGER.error("Nextcloud setup failed - Check configuration")
|
_LOGGER.error("Nextcloud setup failed - Check configuration")
|
||||||
|
|
||||||
hass.data[DOMAIN] = get_data_points(ncm.data)
|
hass.data[DOMAIN] = get_data_points(ncm.data)
|
||||||
|
hass.data[DOMAIN]["instance"] = conf[CONF_URL]
|
||||||
|
|
||||||
def nextcloud_update(event_time):
|
def nextcloud_update(event_time):
|
||||||
"""Update data from nextcloud api."""
|
"""Update data from nextcloud api."""
|
||||||
|
@ -77,6 +77,8 @@ def _precheck_image(image, opts):
|
|||||||
if imgfmt not in ("PNG", "JPEG"):
|
if imgfmt not in ("PNG", "JPEG"):
|
||||||
_LOGGER.warning("Image is of unsupported type: %s", imgfmt)
|
_LOGGER.warning("Image is of unsupported type: %s", imgfmt)
|
||||||
raise ValueError()
|
raise ValueError()
|
||||||
|
if not img.mode == "RGB":
|
||||||
|
img = img.convert("RGB")
|
||||||
return img
|
return img
|
||||||
|
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ class ShellyDeviceWrapper(update_coordinator.DataUpdateCoordinator):
|
|||||||
try:
|
try:
|
||||||
async with async_timeout.timeout(5):
|
async with async_timeout.timeout(5):
|
||||||
return await self.device.update()
|
return await self.device.update()
|
||||||
except aiocoap_error.Error as err:
|
except (aiocoap_error.Error, OSError) as err:
|
||||||
raise update_coordinator.UpdateFailed("Error fetching data") from err
|
raise update_coordinator.UpdateFailed("Error fetching data") from err
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -267,7 +267,8 @@ class SolarEdgeStorageLevelSensor(SolarEdgeSensor):
|
|||||||
"""Get the latest inventory data and update state and attributes."""
|
"""Get the latest inventory data and update state and attributes."""
|
||||||
self.data_service.update()
|
self.data_service.update()
|
||||||
attr = self.data_service.attributes.get(self._json_key)
|
attr = self.data_service.attributes.get(self._json_key)
|
||||||
self._state = attr["soc"]
|
if attr and "soc" in attr:
|
||||||
|
self._state = attr["soc"]
|
||||||
|
|
||||||
|
|
||||||
class SolarEdgeDataService:
|
class SolarEdgeDataService:
|
||||||
|
@ -77,6 +77,9 @@ def _stream_worker_internal(hass, stream, quit_event):
|
|||||||
# compatible with empty_moov and manual bitstream filters not in PyAV
|
# compatible with empty_moov and manual bitstream filters not in PyAV
|
||||||
if container.format.name in {"hls", "mpegts"}:
|
if container.format.name in {"hls", "mpegts"}:
|
||||||
audio_stream = None
|
audio_stream = None
|
||||||
|
# Some audio streams do not have a profile and throw errors when remuxing
|
||||||
|
if audio_stream and audio_stream.profile is None:
|
||||||
|
audio_stream = None
|
||||||
|
|
||||||
# The presentation timestamps of the first packet in each stream we receive
|
# The presentation timestamps of the first packet in each stream we receive
|
||||||
# Use to adjust before muxing or outputting, but we don't adjust internally
|
# Use to adjust before muxing or outputting, but we don't adjust internally
|
||||||
@ -113,7 +116,11 @@ def _stream_worker_internal(hass, stream, quit_event):
|
|||||||
# Get to first video keyframe
|
# Get to first video keyframe
|
||||||
while first_packet[video_stream] is None:
|
while first_packet[video_stream] is None:
|
||||||
packet = next(container.demux())
|
packet = next(container.demux())
|
||||||
if packet.stream == video_stream and packet.is_keyframe:
|
if (
|
||||||
|
packet.stream == video_stream
|
||||||
|
and packet.is_keyframe
|
||||||
|
and packet.dts is not None
|
||||||
|
):
|
||||||
first_packet[video_stream] = packet
|
first_packet[video_stream] = packet
|
||||||
initial_packets.append(packet)
|
initial_packets.append(packet)
|
||||||
# Get first_pts from subsequent frame to first keyframe
|
# Get first_pts from subsequent frame to first keyframe
|
||||||
@ -121,6 +128,8 @@ def _stream_worker_internal(hass, stream, quit_event):
|
|||||||
[pts is None for pts in {**first_packet, **first_pts}.values()]
|
[pts is None for pts in {**first_packet, **first_pts}.values()]
|
||||||
) and (len(initial_packets) < PACKETS_TO_WAIT_FOR_AUDIO):
|
) and (len(initial_packets) < PACKETS_TO_WAIT_FOR_AUDIO):
|
||||||
packet = next(container.demux((video_stream, audio_stream)))
|
packet = next(container.demux((video_stream, audio_stream)))
|
||||||
|
if packet.dts is None:
|
||||||
|
continue # Discard packets with no dts
|
||||||
if (
|
if (
|
||||||
first_packet[packet.stream] is None
|
first_packet[packet.stream] is None
|
||||||
): # actually video already found above so only for audio
|
): # actually video already found above so only for audio
|
||||||
|
@ -67,7 +67,7 @@ class Device:
|
|||||||
"""Create UPnP/IGD device."""
|
"""Create UPnP/IGD device."""
|
||||||
# build async_upnp_client requester
|
# build async_upnp_client requester
|
||||||
session = async_get_clientsession(hass)
|
session = async_get_clientsession(hass)
|
||||||
requester = AiohttpSessionRequester(session, True)
|
requester = AiohttpSessionRequester(session, True, 10)
|
||||||
|
|
||||||
# create async_upnp_client device
|
# create async_upnp_client device
|
||||||
factory = UpnpFactory(requester, disable_state_variable_validation=True)
|
factory = UpnpFactory(requester, disable_state_variable_validation=True)
|
||||||
|
@ -305,7 +305,9 @@ class LgWebOSMediaPlayerEntity(MediaPlayerEntity):
|
|||||||
"""Flag media player features that are supported."""
|
"""Flag media player features that are supported."""
|
||||||
supported = SUPPORT_WEBOSTV
|
supported = SUPPORT_WEBOSTV
|
||||||
|
|
||||||
if self._client.sound_output == "external_arc":
|
if (self._client.sound_output == "external_arc") or (
|
||||||
|
self._client.sound_output == "external_speaker"
|
||||||
|
):
|
||||||
supported = supported | SUPPORT_WEBOSTV_VOLUME
|
supported = supported | SUPPORT_WEBOSTV_VOLUME
|
||||||
elif self._client.sound_output != "lineout":
|
elif self._client.sound_output != "lineout":
|
||||||
supported = supported | SUPPORT_WEBOSTV_VOLUME | SUPPORT_VOLUME_SET
|
supported = supported | SUPPORT_WEBOSTV_VOLUME | SUPPORT_VOLUME_SET
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""Constants used by Home Assistant components."""
|
"""Constants used by Home Assistant components."""
|
||||||
MAJOR_VERSION = 0
|
MAJOR_VERSION = 0
|
||||||
MINOR_VERSION = 115
|
MINOR_VERSION = 115
|
||||||
PATCH_VERSION = "2"
|
PATCH_VERSION = "3"
|
||||||
__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, 7, 1)
|
REQUIRED_PYTHON_VER = (3, 7, 1)
|
||||||
|
@ -13,7 +13,7 @@ defusedxml==0.6.0
|
|||||||
distro==1.5.0
|
distro==1.5.0
|
||||||
emoji==0.5.4
|
emoji==0.5.4
|
||||||
hass-nabucasa==0.37.0
|
hass-nabucasa==0.37.0
|
||||||
home-assistant-frontend==20200918.0
|
home-assistant-frontend==20200918.2
|
||||||
importlib-metadata==1.6.0;python_version<'3.8'
|
importlib-metadata==1.6.0;python_version<'3.8'
|
||||||
jinja2>=2.11.2
|
jinja2>=2.11.2
|
||||||
netdisco==2.8.2
|
netdisco==2.8.2
|
||||||
|
@ -102,7 +102,7 @@ YesssSMS==0.4.1
|
|||||||
abodepy==1.1.0
|
abodepy==1.1.0
|
||||||
|
|
||||||
# homeassistant.components.accuweather
|
# homeassistant.components.accuweather
|
||||||
accuweather==0.0.10
|
accuweather==0.0.11
|
||||||
|
|
||||||
# homeassistant.components.mcp23017
|
# homeassistant.components.mcp23017
|
||||||
adafruit-blinka==3.9.0
|
adafruit-blinka==3.9.0
|
||||||
@ -309,7 +309,7 @@ av==8.0.2
|
|||||||
avri-api==0.1.7
|
avri-api==0.1.7
|
||||||
|
|
||||||
# homeassistant.components.axis
|
# homeassistant.components.axis
|
||||||
axis==35
|
axis==37
|
||||||
|
|
||||||
# homeassistant.components.azure_event_hub
|
# homeassistant.components.azure_event_hub
|
||||||
azure-eventhub==5.1.0
|
azure-eventhub==5.1.0
|
||||||
@ -669,7 +669,7 @@ glances_api==0.2.0
|
|||||||
gntp==1.0.3
|
gntp==1.0.3
|
||||||
|
|
||||||
# homeassistant.components.gogogate2
|
# homeassistant.components.gogogate2
|
||||||
gogogate2-api==2.0.2
|
gogogate2-api==2.0.3
|
||||||
|
|
||||||
# homeassistant.components.google
|
# homeassistant.components.google
|
||||||
google-api-python-client==1.6.4
|
google-api-python-client==1.6.4
|
||||||
@ -747,7 +747,7 @@ hole==0.5.1
|
|||||||
holidays==0.10.3
|
holidays==0.10.3
|
||||||
|
|
||||||
# homeassistant.components.frontend
|
# homeassistant.components.frontend
|
||||||
home-assistant-frontend==20200918.0
|
home-assistant-frontend==20200918.2
|
||||||
|
|
||||||
# homeassistant.components.zwave
|
# homeassistant.components.zwave
|
||||||
homeassistant-pyozw==0.1.10
|
homeassistant-pyozw==0.1.10
|
||||||
@ -1401,7 +1401,7 @@ pyialarm==0.3
|
|||||||
pyicloud==0.9.7
|
pyicloud==0.9.7
|
||||||
|
|
||||||
# homeassistant.components.insteon
|
# homeassistant.components.insteon
|
||||||
pyinsteon==1.0.7
|
pyinsteon==1.0.8
|
||||||
|
|
||||||
# homeassistant.components.intesishome
|
# homeassistant.components.intesishome
|
||||||
pyintesishome==1.7.5
|
pyintesishome==1.7.5
|
||||||
|
@ -45,7 +45,7 @@ YesssSMS==0.4.1
|
|||||||
abodepy==1.1.0
|
abodepy==1.1.0
|
||||||
|
|
||||||
# homeassistant.components.accuweather
|
# homeassistant.components.accuweather
|
||||||
accuweather==0.0.10
|
accuweather==0.0.11
|
||||||
|
|
||||||
# homeassistant.components.androidtv
|
# homeassistant.components.androidtv
|
||||||
adb-shell[async]==0.2.1
|
adb-shell[async]==0.2.1
|
||||||
@ -174,7 +174,7 @@ av==8.0.2
|
|||||||
avri-api==0.1.7
|
avri-api==0.1.7
|
||||||
|
|
||||||
# homeassistant.components.axis
|
# homeassistant.components.axis
|
||||||
axis==35
|
axis==37
|
||||||
|
|
||||||
# homeassistant.components.azure_event_hub
|
# homeassistant.components.azure_event_hub
|
||||||
azure-eventhub==5.1.0
|
azure-eventhub==5.1.0
|
||||||
@ -334,7 +334,7 @@ gios==0.1.4
|
|||||||
glances_api==0.2.0
|
glances_api==0.2.0
|
||||||
|
|
||||||
# homeassistant.components.gogogate2
|
# homeassistant.components.gogogate2
|
||||||
gogogate2-api==2.0.2
|
gogogate2-api==2.0.3
|
||||||
|
|
||||||
# homeassistant.components.google
|
# homeassistant.components.google
|
||||||
google-api-python-client==1.6.4
|
google-api-python-client==1.6.4
|
||||||
@ -370,7 +370,7 @@ hole==0.5.1
|
|||||||
holidays==0.10.3
|
holidays==0.10.3
|
||||||
|
|
||||||
# homeassistant.components.frontend
|
# homeassistant.components.frontend
|
||||||
home-assistant-frontend==20200918.0
|
home-assistant-frontend==20200918.2
|
||||||
|
|
||||||
# homeassistant.components.zwave
|
# homeassistant.components.zwave
|
||||||
homeassistant-pyozw==0.1.10
|
homeassistant-pyozw==0.1.10
|
||||||
@ -674,7 +674,7 @@ pyhomematic==0.1.68
|
|||||||
pyicloud==0.9.7
|
pyicloud==0.9.7
|
||||||
|
|
||||||
# homeassistant.components.insteon
|
# homeassistant.components.insteon
|
||||||
pyinsteon==1.0.7
|
pyinsteon==1.0.8
|
||||||
|
|
||||||
# homeassistant.components.ipma
|
# homeassistant.components.ipma
|
||||||
pyipma==2.0.5
|
pyipma==2.0.5
|
||||||
|
@ -737,6 +737,11 @@ async def test_reload_with_base_integration_platform_not_setup(hass):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
hass.states.async_set("light.master_hall_lights", STATE_ON)
|
||||||
|
hass.states.async_set("light.master_hall_lights_2", STATE_OFF)
|
||||||
|
|
||||||
|
hass.states.async_set("light.outside_patio_lights", STATE_OFF)
|
||||||
|
hass.states.async_set("light.outside_patio_lights_2", STATE_OFF)
|
||||||
|
|
||||||
yaml_path = path.join(
|
yaml_path = path.join(
|
||||||
_get_fixtures_base_path(),
|
_get_fixtures_base_path(),
|
||||||
@ -755,6 +760,8 @@ async def test_reload_with_base_integration_platform_not_setup(hass):
|
|||||||
assert hass.states.get("light.light_group") is None
|
assert hass.states.get("light.light_group") is None
|
||||||
assert hass.states.get("light.master_hall_lights_g") is not None
|
assert hass.states.get("light.master_hall_lights_g") is not None
|
||||||
assert hass.states.get("light.outside_patio_lights_g") is not None
|
assert hass.states.get("light.outside_patio_lights_g") is not None
|
||||||
|
assert hass.states.get("light.master_hall_lights_g").state == STATE_ON
|
||||||
|
assert hass.states.get("light.outside_patio_lights_g").state == STATE_OFF
|
||||||
|
|
||||||
|
|
||||||
def _get_fixtures_base_path():
|
def _get_fixtures_base_path():
|
||||||
|
@ -538,6 +538,39 @@ async def test_if_fires_on_entity_change_with_for_without_to(hass, calls):
|
|||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_if_does_not_fires_on_entity_change_with_for_without_to_2(hass, calls):
|
||||||
|
"""Test for firing on entity change with for."""
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass,
|
||||||
|
automation.DOMAIN,
|
||||||
|
{
|
||||||
|
automation.DOMAIN: {
|
||||||
|
"trigger": {
|
||||||
|
"platform": "state",
|
||||||
|
"entity_id": "test.entity",
|
||||||
|
"for": {"seconds": 5},
|
||||||
|
},
|
||||||
|
"action": {"service": "test.automation"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
utcnow = dt_util.utcnow()
|
||||||
|
with patch("homeassistant.core.dt_util.utcnow") as mock_utcnow:
|
||||||
|
mock_utcnow.return_value = utcnow
|
||||||
|
|
||||||
|
for i in range(10):
|
||||||
|
hass.states.async_set("test.entity", str(i))
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
mock_utcnow.return_value += timedelta(seconds=1)
|
||||||
|
async_fire_time_changed(hass, mock_utcnow.return_value)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(calls) == 0
|
||||||
|
|
||||||
|
|
||||||
async def test_if_fires_on_entity_creation_and_removal(hass, calls):
|
async def test_if_fires_on_entity_creation_and_removal(hass, calls):
|
||||||
"""Test for firing on entity creation and removal, with to/from constraints."""
|
"""Test for firing on entity creation and removal, with to/from constraints."""
|
||||||
# set automations for multiple combinations to/from
|
# set automations for multiple combinations to/from
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
"""The tests for the time_pattern automation."""
|
"""The tests for the time_pattern automation."""
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
@ -123,6 +125,39 @@ async def test_if_fires_when_second_matches(hass, calls):
|
|||||||
assert len(calls) == 1
|
assert len(calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_if_fires_when_second_as_string_matches(hass, calls):
|
||||||
|
"""Test for firing if seconds are matching."""
|
||||||
|
now = dt_util.utcnow()
|
||||||
|
time_that_will_not_match_right_away = dt_util.utcnow().replace(
|
||||||
|
year=now.year + 1, second=15
|
||||||
|
)
|
||||||
|
with patch(
|
||||||
|
"homeassistant.util.dt.utcnow", return_value=time_that_will_not_match_right_away
|
||||||
|
):
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass,
|
||||||
|
automation.DOMAIN,
|
||||||
|
{
|
||||||
|
automation.DOMAIN: {
|
||||||
|
"trigger": {
|
||||||
|
"platform": "time_pattern",
|
||||||
|
"hours": "*",
|
||||||
|
"minutes": "*",
|
||||||
|
"seconds": "30",
|
||||||
|
},
|
||||||
|
"action": {"service": "test.automation"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
async_fire_time_changed(
|
||||||
|
hass, time_that_will_not_match_right_away + timedelta(seconds=15)
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(calls) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_if_fires_when_all_matches(hass, calls):
|
async def test_if_fires_when_all_matches(hass, calls):
|
||||||
"""Test for firing if everything matches."""
|
"""Test for firing if everything matches."""
|
||||||
now = dt_util.utcnow()
|
now = dt_util.utcnow()
|
||||||
|
@ -165,6 +165,51 @@ async def test_form_valid_ws_port(hass, user_flow):
|
|||||||
assert len(mock_setup_entry.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_form_empty_ws_port(hass, user_flow):
|
||||||
|
"""Test we handle an empty websocket port input."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.kodi.config_flow.Kodi.ping",
|
||||||
|
return_value=True,
|
||||||
|
), patch.object(
|
||||||
|
MockWSConnection,
|
||||||
|
"connect",
|
||||||
|
AsyncMock(side_effect=CannotConnectError),
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.kodi.config_flow.get_kodi_connection",
|
||||||
|
new=get_kodi_connection,
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_configure(user_flow, TEST_HOST)
|
||||||
|
|
||||||
|
assert result["type"] == "form"
|
||||||
|
assert result["step_id"] == "ws_port"
|
||||||
|
assert result["errors"] == {}
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.kodi.async_setup", return_value=True
|
||||||
|
) as mock_setup, patch(
|
||||||
|
"homeassistant.components.kodi.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
) as mock_setup_entry:
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], {"ws_port": 0}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == "create_entry"
|
||||||
|
assert result["title"] == TEST_HOST["host"]
|
||||||
|
assert result["data"] == {
|
||||||
|
**TEST_HOST,
|
||||||
|
"ws_port": None,
|
||||||
|
"password": None,
|
||||||
|
"username": None,
|
||||||
|
"name": None,
|
||||||
|
"timeout": DEFAULT_TIMEOUT,
|
||||||
|
}
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(mock_setup.mock_calls) == 1
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_form_invalid_auth(hass, user_flow):
|
async def test_form_invalid_auth(hass, user_flow):
|
||||||
"""Test we handle invalid auth."""
|
"""Test we handle invalid auth."""
|
||||||
with patch(
|
with patch(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user