Merge pull request #75243 from home-assistant/rc

This commit is contained in:
Paulus Schoutsen 2022-07-14 22:46:51 -07:00 committed by GitHub
commit 4e29bdf715
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 289 additions and 113 deletions

View File

@ -129,8 +129,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/binary_sensor/ @home-assistant/core /homeassistant/components/binary_sensor/ @home-assistant/core
/tests/components/binary_sensor/ @home-assistant/core /tests/components/binary_sensor/ @home-assistant/core
/homeassistant/components/bizkaibus/ @UgaitzEtxebarria /homeassistant/components/bizkaibus/ @UgaitzEtxebarria
/homeassistant/components/blebox/ @bbx-a @bbx-jp @riokuu /homeassistant/components/blebox/ @bbx-a @riokuu
/tests/components/blebox/ @bbx-a @bbx-jp @riokuu /tests/components/blebox/ @bbx-a @riokuu
/homeassistant/components/blink/ @fronzbot /homeassistant/components/blink/ @fronzbot
/tests/components/blink/ @fronzbot /tests/components/blink/ @fronzbot
/homeassistant/components/blueprint/ @home-assistant/core /homeassistant/components/blueprint/ @home-assistant/core

View File

@ -12,7 +12,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN from .const import CLIENT_ID, DOMAIN
_LOGGER: Final = logging.getLogger(__name__) _LOGGER: Final = logging.getLogger(__name__)
@ -23,7 +23,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up platform from a ConfigEntry.""" """Set up platform from a ConfigEntry."""
username = entry.data[CONF_USERNAME] username = entry.data[CONF_USERNAME]
password = entry.data[CONF_PASSWORD] password = entry.data[CONF_PASSWORD]
acc = AladdinConnectClient(username, password, async_get_clientsession(hass)) acc = AladdinConnectClient(
username, password, async_get_clientsession(hass), CLIENT_ID
)
try: try:
if not await acc.login(): if not await acc.login():
raise ConfigEntryAuthFailed("Incorrect Password") raise ConfigEntryAuthFailed("Incorrect Password")

View File

@ -18,7 +18,7 @@ from homeassistant.data_entry_flow import FlowResult
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN from .const import CLIENT_ID, DOMAIN
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -38,7 +38,10 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> None:
Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user. Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user.
""" """
acc = AladdinConnectClient( acc = AladdinConnectClient(
data[CONF_USERNAME], data[CONF_PASSWORD], async_get_clientsession(hass) data[CONF_USERNAME],
data[CONF_PASSWORD],
async_get_clientsession(hass),
CLIENT_ID,
) )
login = await acc.login() login = await acc.login()
await acc.close() await acc.close()

View File

@ -18,3 +18,4 @@ STATES_MAP: Final[dict[str, str]] = {
DOMAIN = "aladdin_connect" DOMAIN = "aladdin_connect"
SUPPORTED_FEATURES: Final = CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE SUPPORTED_FEATURES: Final = CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE
CLIENT_ID = "1000"

View File

@ -2,7 +2,7 @@
"domain": "aladdin_connect", "domain": "aladdin_connect",
"name": "Aladdin Connect", "name": "Aladdin Connect",
"documentation": "https://www.home-assistant.io/integrations/aladdin_connect", "documentation": "https://www.home-assistant.io/integrations/aladdin_connect",
"requirements": ["AIOAladdinConnect==0.1.23"], "requirements": ["AIOAladdinConnect==0.1.25"],
"codeowners": ["@mkmer"], "codeowners": ["@mkmer"],
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["aladdin_connect"], "loggers": ["aladdin_connect"],

View File

@ -86,7 +86,9 @@ async def async_enable_proactive_mode(hass, smart_home_config):
return return
if should_doorbell: if should_doorbell:
if new_state.state == STATE_ON: if new_state.state == STATE_ON and (
old_state is None or old_state.state != STATE_ON
):
await async_send_doorbell_event_message( await async_send_doorbell_event_message(
hass, smart_home_config, alexa_changed_entity hass, smart_home_config, alexa_changed_entity
) )

View File

@ -3,13 +3,7 @@
from homeassistant.components.cover import CoverDeviceClass from homeassistant.components.cover import CoverDeviceClass
from homeassistant.components.sensor import SensorDeviceClass from homeassistant.components.sensor import SensorDeviceClass
from homeassistant.components.switch import SwitchDeviceClass from homeassistant.components.switch import SwitchDeviceClass
from homeassistant.const import ( from homeassistant.const import TEMP_CELSIUS
STATE_CLOSED,
STATE_CLOSING,
STATE_OPEN,
STATE_OPENING,
TEMP_CELSIUS,
)
DOMAIN = "blebox" DOMAIN = "blebox"
PRODUCT = "product" PRODUCT = "product"
@ -30,19 +24,6 @@ BLEBOX_TO_HASS_DEVICE_CLASSES = {
"temperature": SensorDeviceClass.TEMPERATURE, "temperature": SensorDeviceClass.TEMPERATURE,
} }
BLEBOX_TO_HASS_COVER_STATES = {
None: None,
0: STATE_CLOSING, # moving down
1: STATE_OPENING, # moving up
2: STATE_OPEN, # manually stopped
3: STATE_CLOSED, # lower limit
4: STATE_OPEN, # upper limit / open
# gateController
5: STATE_OPEN, # overload
6: STATE_OPEN, # motor failure
# 7 is not used
8: STATE_OPEN, # safety stop
}
BLEBOX_TO_UNIT_MAP = {"celsius": TEMP_CELSIUS} BLEBOX_TO_UNIT_MAP = {"celsius": TEMP_CELSIUS}

View File

@ -9,12 +9,26 @@ from homeassistant.components.cover import (
CoverEntityFeature, CoverEntityFeature,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_CLOSED, STATE_CLOSING, STATE_OPENING from homeassistant.const import STATE_CLOSED, STATE_CLOSING, STATE_OPEN, STATE_OPENING
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import BleBoxEntity, create_blebox_entities from . import BleBoxEntity, create_blebox_entities
from .const import BLEBOX_TO_HASS_COVER_STATES, BLEBOX_TO_HASS_DEVICE_CLASSES from .const import BLEBOX_TO_HASS_DEVICE_CLASSES
BLEBOX_TO_HASS_COVER_STATES = {
None: None,
0: STATE_CLOSING, # moving down
1: STATE_OPENING, # moving up
2: STATE_OPEN, # manually stopped
3: STATE_CLOSED, # lower limit
4: STATE_OPEN, # upper limit / open
# gateController
5: STATE_OPEN, # overload
6: STATE_OPEN, # motor failure
# 7 is not used
8: STATE_OPEN, # safety stop
}
async def async_setup_entry( async def async_setup_entry(

View File

@ -4,7 +4,6 @@ from __future__ import annotations
from datetime import timedelta from datetime import timedelta
import logging import logging
from blebox_uniapi.error import BadOnValueError
import blebox_uniapi.light import blebox_uniapi.light
from blebox_uniapi.light import BleboxColorMode from blebox_uniapi.light import BleboxColorMode
@ -160,16 +159,21 @@ class BleBoxLightEntity(BleBoxEntity, LightEntity):
else: else:
value = feature.apply_brightness(value, brightness) value = feature.apply_brightness(value, brightness)
try:
await self._feature.async_on(value)
except ValueError as exc:
raise ValueError(
f"Turning on '{self.name}' failed: Bad value {value}"
) from exc
if effect is not None: if effect is not None:
effect_value = self.effect_list.index(effect)
await self._feature.async_api_command("effect", effect_value)
else:
try: try:
await self._feature.async_on(value) effect_value = self.effect_list.index(effect)
except BadOnValueError as ex: await self._feature.async_api_command("effect", effect_value)
_LOGGER.error( except ValueError as exc:
"Turning on '%s' failed: Bad value %s (%s)", self.name, value, ex raise ValueError(
) f"Turning on with effect '{self.name}' failed: {effect} not in effect list."
) from exc
async def async_turn_off(self, **kwargs): async def async_turn_off(self, **kwargs):
"""Turn the light off.""" """Turn the light off."""

View File

@ -3,8 +3,8 @@
"name": "BleBox devices", "name": "BleBox devices",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/blebox", "documentation": "https://www.home-assistant.io/integrations/blebox",
"requirements": ["blebox_uniapi==2.0.0"], "requirements": ["blebox_uniapi==2.0.1"],
"codeowners": ["@bbx-a", "@bbx-jp", "@riokuu"], "codeowners": ["@bbx-a", "@riokuu"],
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["blebox_uniapi"] "loggers": ["blebox_uniapi"]
} }

View File

@ -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==20220707.0"], "requirements": ["home-assistant-frontend==20220707.1"],
"dependencies": [ "dependencies": [
"api", "api",
"auth", "auth",

View File

@ -8,7 +8,7 @@ from homeassistant.components.sensor import (
SensorStateClass, SensorStateClass,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import PERCENTAGE, POWER_KILO_WATT from homeassistant.const import PERCENTAGE, POWER_WATT
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
@ -26,7 +26,7 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
), ),
SensorEntityDescription( SensorEntityDescription(
key="Power", key="Power",
native_unit_of_measurement=POWER_KILO_WATT, native_unit_of_measurement=POWER_WATT,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
device_class=SensorDeviceClass.POWER, device_class=SensorDeviceClass.POWER,
), ),

View File

@ -3,7 +3,7 @@
"name": "Litter-Robot", "name": "Litter-Robot",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/litterrobot", "documentation": "https://www.home-assistant.io/integrations/litterrobot",
"requirements": ["pylitterbot==2022.3.0"], "requirements": ["pylitterbot==2022.7.0"],
"codeowners": ["@natekspencer"], "codeowners": ["@natekspencer"],
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["pylitterbot"] "loggers": ["pylitterbot"]

View File

@ -11,8 +11,7 @@
"dhcp": [ "dhcp": [
{ "macaddress": "18B430*" }, { "macaddress": "18B430*" },
{ "macaddress": "641666*" }, { "macaddress": "641666*" },
{ "macaddress": "D8EB46*" }, { "macaddress": "D8EB46*" }
{ "macaddress": "1C53F9*" }
], ],
"iot_class": "cloud_push", "iot_class": "cloud_push",
"loggers": ["google_nest_sdm", "nest"] "loggers": ["google_nest_sdm", "nest"]

View File

@ -57,9 +57,15 @@ from .const import (
SOURCE_TIMEOUT, SOURCE_TIMEOUT,
STREAM_RESTART_INCREMENT, STREAM_RESTART_INCREMENT,
STREAM_RESTART_RESET_TIME, STREAM_RESTART_RESET_TIME,
TARGET_SEGMENT_DURATION_NON_LL_HLS,
) )
from .core import PROVIDERS, IdleTimer, KeyFrameConverter, StreamOutput, StreamSettings from .core import (
PROVIDERS,
STREAM_SETTINGS_NON_LL_HLS,
IdleTimer,
KeyFrameConverter,
StreamOutput,
StreamSettings,
)
from .diagnostics import Diagnostics from .diagnostics import Diagnostics
from .hls import HlsStreamOutput, async_setup_hls from .hls import HlsStreamOutput, async_setup_hls
@ -224,14 +230,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
hls_part_timeout=2 * conf[CONF_PART_DURATION], hls_part_timeout=2 * conf[CONF_PART_DURATION],
) )
else: else:
hass.data[DOMAIN][ATTR_SETTINGS] = StreamSettings( hass.data[DOMAIN][ATTR_SETTINGS] = STREAM_SETTINGS_NON_LL_HLS
ll_hls=False,
min_segment_duration=TARGET_SEGMENT_DURATION_NON_LL_HLS
- SEGMENT_DURATION_ADJUSTER,
part_target_duration=TARGET_SEGMENT_DURATION_NON_LL_HLS,
hls_advance_part_limit=3,
hls_part_timeout=TARGET_SEGMENT_DURATION_NON_LL_HLS,
)
# Setup HLS # Setup HLS
hls_endpoint = async_setup_hls(hass) hls_endpoint = async_setup_hls(hass)

View File

@ -5,6 +5,7 @@ import asyncio
from collections import deque from collections import deque
from collections.abc import Callable, Coroutine, Iterable from collections.abc import Callable, Coroutine, Iterable
import datetime import datetime
import logging
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING, Any
from aiohttp import web from aiohttp import web
@ -16,13 +17,20 @@ from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
from homeassistant.helpers.event import async_call_later from homeassistant.helpers.event import async_call_later
from homeassistant.util.decorator import Registry from homeassistant.util.decorator import Registry
from .const import ATTR_STREAMS, DOMAIN from .const import (
ATTR_STREAMS,
DOMAIN,
SEGMENT_DURATION_ADJUSTER,
TARGET_SEGMENT_DURATION_NON_LL_HLS,
)
if TYPE_CHECKING: if TYPE_CHECKING:
from av import CodecContext, Packet from av import CodecContext, Packet
from . import Stream from . import Stream
_LOGGER = logging.getLogger(__name__)
PROVIDERS: Registry[str, type[StreamOutput]] = Registry() PROVIDERS: Registry[str, type[StreamOutput]] = Registry()
@ -37,6 +45,15 @@ class StreamSettings:
hls_part_timeout: float = attr.ib() hls_part_timeout: float = attr.ib()
STREAM_SETTINGS_NON_LL_HLS = StreamSettings(
ll_hls=False,
min_segment_duration=TARGET_SEGMENT_DURATION_NON_LL_HLS - SEGMENT_DURATION_ADJUSTER,
part_target_duration=TARGET_SEGMENT_DURATION_NON_LL_HLS,
hls_advance_part_limit=3,
hls_part_timeout=TARGET_SEGMENT_DURATION_NON_LL_HLS,
)
@attr.s(slots=True) @attr.s(slots=True)
class Part: class Part:
"""Represent a segment part.""" """Represent a segment part."""
@ -426,12 +443,22 @@ class KeyFrameConverter:
return return
packet = self.packet packet = self.packet
self.packet = None self.packet = None
# decode packet (flush afterwards) for _ in range(2): # Retry once if codec context needs to be flushed
frames = self._codec_context.decode(packet) try:
for _i in range(2): # decode packet (flush afterwards)
if frames: frames = self._codec_context.decode(packet)
for _i in range(2):
if frames:
break
frames = self._codec_context.decode(None)
break break
frames = self._codec_context.decode(None) except EOFError:
_LOGGER.debug("Codec context needs flushing, attempting to reopen")
self._codec_context.close()
self._codec_context.open()
else:
_LOGGER.debug("Unable to decode keyframe")
return
if frames: if frames:
frame = frames[0] frame = frames[0]
if width and height: if width and height:

View File

@ -2,6 +2,10 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Generator from collections.abc import Generator
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from io import BytesIO
def find_box( def find_box(
@ -135,3 +139,11 @@ def get_codec_string(mp4_bytes: bytes) -> str:
codecs.append(codec) codecs.append(codec)
return ",".join(codecs) return ",".join(codecs)
def read_init(bytes_io: BytesIO) -> bytes:
"""Read the init from a mp4 file."""
bytes_io.seek(24)
moov_len = int.from_bytes(bytes_io.read(4), byteorder="big")
bytes_io.seek(0)
return bytes_io.read(24 + moov_len)

View File

@ -5,11 +5,12 @@ from collections import defaultdict, deque
from collections.abc import Callable, Generator, Iterator, Mapping from collections.abc import Callable, Generator, Iterator, Mapping
import contextlib import contextlib
import datetime import datetime
from io import BytesIO from io import SEEK_END, BytesIO
import logging import logging
from threading import Event from threading import Event
from typing import Any, cast from typing import Any, cast
import attr
import av import av
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -24,8 +25,16 @@ from .const import (
SEGMENT_CONTAINER_FORMAT, SEGMENT_CONTAINER_FORMAT,
SOURCE_TIMEOUT, SOURCE_TIMEOUT,
) )
from .core import KeyFrameConverter, Part, Segment, StreamOutput, StreamSettings from .core import (
STREAM_SETTINGS_NON_LL_HLS,
KeyFrameConverter,
Part,
Segment,
StreamOutput,
StreamSettings,
)
from .diagnostics import Diagnostics from .diagnostics import Diagnostics
from .fmp4utils import read_init
from .hls import HlsStreamOutput from .hls import HlsStreamOutput
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -108,7 +117,7 @@ class StreamMuxer:
hass: HomeAssistant, hass: HomeAssistant,
video_stream: av.video.VideoStream, video_stream: av.video.VideoStream,
audio_stream: av.audio.stream.AudioStream | None, audio_stream: av.audio.stream.AudioStream | None,
audio_bsf: av.BitStreamFilterContext | None, audio_bsf: av.BitStreamFilter | None,
stream_state: StreamState, stream_state: StreamState,
stream_settings: StreamSettings, stream_settings: StreamSettings,
) -> None: ) -> None:
@ -120,6 +129,7 @@ class StreamMuxer:
self._input_video_stream: av.video.VideoStream = video_stream self._input_video_stream: av.video.VideoStream = video_stream
self._input_audio_stream: av.audio.stream.AudioStream | None = audio_stream self._input_audio_stream: av.audio.stream.AudioStream | None = audio_stream
self._audio_bsf = audio_bsf self._audio_bsf = audio_bsf
self._audio_bsf_context: av.BitStreamFilterContext = None
self._output_video_stream: av.video.VideoStream = None self._output_video_stream: av.video.VideoStream = None
self._output_audio_stream: av.audio.stream.AudioStream | None = None self._output_audio_stream: av.audio.stream.AudioStream | None = None
self._segment: Segment | None = None self._segment: Segment | None = None
@ -151,7 +161,7 @@ class StreamMuxer:
**{ **{
# Removed skip_sidx - see https://github.com/home-assistant/core/pull/39970 # Removed skip_sidx - see https://github.com/home-assistant/core/pull/39970
# "cmaf" flag replaces several of the movflags used, but too recent to use for now # "cmaf" flag replaces several of the movflags used, but too recent to use for now
"movflags": "frag_custom+empty_moov+default_base_moof+frag_discont+negative_cts_offsets+skip_trailer", "movflags": "frag_custom+empty_moov+default_base_moof+frag_discont+negative_cts_offsets+skip_trailer+delay_moov",
# Sometimes the first segment begins with negative timestamps, and this setting just # Sometimes the first segment begins with negative timestamps, and this setting just
# adjusts the timestamps in the output from that segment to start from 0. Helps from # adjusts the timestamps in the output from that segment to start from 0. Helps from
# having to make some adjustments in test_durations # having to make some adjustments in test_durations
@ -164,7 +174,7 @@ class StreamMuxer:
# Fragment durations may exceed the 15% allowed variance but it seems ok # Fragment durations may exceed the 15% allowed variance but it seems ok
**( **(
{ {
"movflags": "empty_moov+default_base_moof+frag_discont+negative_cts_offsets+skip_trailer", "movflags": "empty_moov+default_base_moof+frag_discont+negative_cts_offsets+skip_trailer+delay_moov",
# Create a fragment every TARGET_PART_DURATION. The data from each fragment is stored in # Create a fragment every TARGET_PART_DURATION. The data from each fragment is stored in
# a "Part" that can be combined with the data from all the other "Part"s, plus an init # a "Part" that can be combined with the data from all the other "Part"s, plus an init
# section, to reconstitute the data in a "Segment". # section, to reconstitute the data in a "Segment".
@ -194,8 +204,11 @@ class StreamMuxer:
# Check if audio is requested # Check if audio is requested
output_astream = None output_astream = None
if input_astream: if input_astream:
if self._audio_bsf:
self._audio_bsf_context = self._audio_bsf.create()
self._audio_bsf_context.set_input_stream(input_astream)
output_astream = container.add_stream( output_astream = container.add_stream(
template=self._audio_bsf or input_astream template=self._audio_bsf_context or input_astream
) )
return container, output_vstream, output_astream return container, output_vstream, output_astream
@ -238,15 +251,29 @@ class StreamMuxer:
self._part_has_keyframe |= packet.is_keyframe self._part_has_keyframe |= packet.is_keyframe
elif packet.stream == self._input_audio_stream: elif packet.stream == self._input_audio_stream:
if self._audio_bsf: if self._audio_bsf_context:
self._audio_bsf.send(packet) self._audio_bsf_context.send(packet)
while packet := self._audio_bsf.recv(): while packet := self._audio_bsf_context.recv():
packet.stream = self._output_audio_stream packet.stream = self._output_audio_stream
self._av_output.mux(packet) self._av_output.mux(packet)
return return
packet.stream = self._output_audio_stream packet.stream = self._output_audio_stream
self._av_output.mux(packet) self._av_output.mux(packet)
def create_segment(self) -> None:
"""Create a segment when the moov is ready."""
self._segment = Segment(
sequence=self._stream_state.sequence,
stream_id=self._stream_state.stream_id,
init=read_init(self._memory_file),
# Fetch the latest StreamOutputs, which may have changed since the
# worker started.
stream_outputs=self._stream_state.outputs,
start_time=self._start_time,
)
self._memory_file_pos = self._memory_file.tell()
self._memory_file.seek(0, SEEK_END)
def check_flush_part(self, packet: av.Packet) -> None: def check_flush_part(self, packet: av.Packet) -> None:
"""Check for and mark a part segment boundary and record its duration.""" """Check for and mark a part segment boundary and record its duration."""
if self._memory_file_pos == self._memory_file.tell(): if self._memory_file_pos == self._memory_file.tell():
@ -254,16 +281,10 @@ class StreamMuxer:
if self._segment is None: if self._segment is None:
# We have our first non-zero byte position. This means the init has just # We have our first non-zero byte position. This means the init has just
# been written. Create a Segment and put it to the queue of each output. # been written. Create a Segment and put it to the queue of each output.
self._segment = Segment( self.create_segment()
sequence=self._stream_state.sequence, # When using delay_moov, the moov is not written until a moof is also ready
stream_id=self._stream_state.stream_id, # Flush the moof
init=self._memory_file.getvalue(), self.flush(packet, last_part=False)
# Fetch the latest StreamOutputs, which may have changed since the
# worker started.
stream_outputs=self._stream_state.outputs,
start_time=self._start_time,
)
self._memory_file_pos = self._memory_file.tell()
else: # These are the ends of the part segments else: # These are the ends of the part segments
self.flush(packet, last_part=False) self.flush(packet, last_part=False)
@ -297,6 +318,10 @@ class StreamMuxer:
# Closing the av_output will write the remaining buffered data to the # Closing the av_output will write the remaining buffered data to the
# memory_file as a new moof/mdat. # memory_file as a new moof/mdat.
self._av_output.close() self._av_output.close()
# With delay_moov, this may be the first time the file pointer has
# moved, so the segment may not yet have been created
if not self._segment:
self.create_segment()
elif not self._part_has_keyframe: elif not self._part_has_keyframe:
# Parts which are not the last part or an independent part should # Parts which are not the last part or an independent part should
# not have durations below 0.85 of the part target duration. # not have durations below 0.85 of the part target duration.
@ -305,6 +330,9 @@ class StreamMuxer:
self._part_start_dts self._part_start_dts
+ 0.85 * self._stream_settings.part_target_duration / packet.time_base, + 0.85 * self._stream_settings.part_target_duration / packet.time_base,
) )
# Undo dts adjustments if we don't have ll_hls
if not self._stream_settings.ll_hls:
adjusted_dts = packet.dts
assert self._segment assert self._segment
self._memory_file.seek(self._memory_file_pos) self._memory_file.seek(self._memory_file_pos)
self._hass.loop.call_soon_threadsafe( self._hass.loop.call_soon_threadsafe(
@ -445,10 +473,7 @@ def get_audio_bitstream_filter(
_LOGGER.debug( _LOGGER.debug(
"ADTS AAC detected. Adding aac_adtstoaac bitstream filter" "ADTS AAC detected. Adding aac_adtstoaac bitstream filter"
) )
bsf = av.BitStreamFilter("aac_adtstoasc") return av.BitStreamFilter("aac_adtstoasc")
bsf_context = bsf.create()
bsf_context.set_input_stream(audio_stream)
return bsf_context
break break
return None return None
@ -489,7 +514,12 @@ def stream_worker(
audio_stream = None audio_stream = None
# Disable ll-hls for hls inputs # Disable ll-hls for hls inputs
if container.format.name == "hls": if container.format.name == "hls":
stream_settings.ll_hls = False for field in attr.fields(StreamSettings):
setattr(
stream_settings,
field.name,
getattr(STREAM_SETTINGS_NON_LL_HLS, field.name),
)
stream_state.diagnostics.set_value("container_format", container.format.name) stream_state.diagnostics.set_value("container_format", container.format.name)
stream_state.diagnostics.set_value("video_codec", video_stream.name) stream_state.diagnostics.set_value("video_codec", video_stream.name)
if audio_stream: if audio_stream:

View File

@ -3,7 +3,7 @@
"name": "UniFi Protect", "name": "UniFi Protect",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/unifiprotect", "documentation": "https://www.home-assistant.io/integrations/unifiprotect",
"requirements": ["pyunifiprotect==4.0.9", "unifi-discovery==1.1.4"], "requirements": ["pyunifiprotect==4.0.10", "unifi-discovery==1.1.4"],
"dependencies": ["http"], "dependencies": ["http"],
"codeowners": ["@briis", "@AngellusMortis", "@bdraco"], "codeowners": ["@briis", "@AngellusMortis", "@bdraco"],
"quality_scale": "platinum", "quality_scale": "platinum",

View File

@ -9,7 +9,7 @@
"pyserial-asyncio==0.6", "pyserial-asyncio==0.6",
"zha-quirks==0.0.77", "zha-quirks==0.0.77",
"zigpy-deconz==0.18.0", "zigpy-deconz==0.18.0",
"zigpy==0.47.2", "zigpy==0.47.3",
"zigpy-xbee==0.15.0", "zigpy-xbee==0.15.0",
"zigpy-zigate==0.9.0", "zigpy-zigate==0.9.0",
"zigpy-znp==0.8.1" "zigpy-znp==0.8.1"

View File

@ -7,7 +7,7 @@ from .backports.enum import StrEnum
MAJOR_VERSION: Final = 2022 MAJOR_VERSION: Final = 2022
MINOR_VERSION: Final = 7 MINOR_VERSION: Final = 7
PATCH_VERSION: Final = "4" PATCH_VERSION: Final = "5"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0)

View File

@ -69,7 +69,6 @@ DHCP: list[dict[str, str | bool]] = [
{'domain': 'nest', 'macaddress': '18B430*'}, {'domain': 'nest', 'macaddress': '18B430*'},
{'domain': 'nest', 'macaddress': '641666*'}, {'domain': 'nest', 'macaddress': '641666*'},
{'domain': 'nest', 'macaddress': 'D8EB46*'}, {'domain': 'nest', 'macaddress': 'D8EB46*'},
{'domain': 'nest', 'macaddress': '1C53F9*'},
{'domain': 'nexia', 'hostname': 'xl857-*', 'macaddress': '000231*'}, {'domain': 'nexia', 'hostname': 'xl857-*', 'macaddress': '000231*'},
{'domain': 'nuheat', 'hostname': 'nuheat', 'macaddress': '002338*'}, {'domain': 'nuheat', 'hostname': 'nuheat', 'macaddress': '002338*'},
{'domain': 'nuki', 'hostname': 'nuki_bridge_*'}, {'domain': 'nuki', 'hostname': 'nuki_bridge_*'},

View File

@ -15,7 +15,7 @@ ciso8601==2.2.0
cryptography==36.0.2 cryptography==36.0.2
fnvhash==0.1.0 fnvhash==0.1.0
hass-nabucasa==0.54.1 hass-nabucasa==0.54.1
home-assistant-frontend==20220707.0 home-assistant-frontend==20220707.1
httpx==0.23.0 httpx==0.23.0
ifaddr==0.1.7 ifaddr==0.1.7
jinja2==3.1.2 jinja2==3.1.2
@ -118,3 +118,7 @@ pydantic!=1.9.1
# Breaks asyncio # Breaks asyncio
# https://github.com/pubnub/python/issues/130 # https://github.com/pubnub/python/issues/130
pubnub!=6.4.0 pubnub!=6.4.0
# Package's __init__.pyi stub has invalid syntax and breaks mypy
# https://github.com/dahlia/iso4217/issues/16
iso4217!=1.10.20220401

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "homeassistant" name = "homeassistant"
version = "2022.7.4" version = "2022.7.5"
license = {text = "Apache-2.0"} license = {text = "Apache-2.0"}
description = "Open-source home automation platform running on Python 3." description = "Open-source home automation platform running on Python 3."
readme = "README.rst" readme = "README.rst"

View File

@ -5,7 +5,7 @@
AEMET-OpenData==0.2.1 AEMET-OpenData==0.2.1
# homeassistant.components.aladdin_connect # homeassistant.components.aladdin_connect
AIOAladdinConnect==0.1.23 AIOAladdinConnect==0.1.25
# homeassistant.components.adax # homeassistant.components.adax
Adax-local==0.1.4 Adax-local==0.1.4
@ -402,7 +402,7 @@ bimmer_connected==0.9.6
bizkaibus==0.1.1 bizkaibus==0.1.1
# homeassistant.components.blebox # homeassistant.components.blebox
blebox_uniapi==2.0.0 blebox_uniapi==2.0.1
# homeassistant.components.blink # homeassistant.components.blink
blinkpy==0.19.0 blinkpy==0.19.0
@ -828,7 +828,7 @@ hole==0.7.0
holidays==0.14.2 holidays==0.14.2
# homeassistant.components.frontend # homeassistant.components.frontend
home-assistant-frontend==20220707.0 home-assistant-frontend==20220707.1
# homeassistant.components.home_connect # homeassistant.components.home_connect
homeconnect==0.7.1 homeconnect==0.7.1
@ -1625,7 +1625,7 @@ pylibrespot-java==0.1.0
pylitejet==0.3.0 pylitejet==0.3.0
# homeassistant.components.litterrobot # homeassistant.components.litterrobot
pylitterbot==2022.3.0 pylitterbot==2022.7.0
# homeassistant.components.lutron_caseta # homeassistant.components.lutron_caseta
pylutron-caseta==0.13.1 pylutron-caseta==0.13.1
@ -1993,7 +1993,7 @@ pytrafikverket==0.2.0.1
pyudev==0.22.0 pyudev==0.22.0
# homeassistant.components.unifiprotect # homeassistant.components.unifiprotect
pyunifiprotect==4.0.9 pyunifiprotect==4.0.10
# homeassistant.components.uptimerobot # homeassistant.components.uptimerobot
pyuptimerobot==22.2.0 pyuptimerobot==22.2.0
@ -2519,7 +2519,7 @@ zigpy-zigate==0.9.0
zigpy-znp==0.8.1 zigpy-znp==0.8.1
# homeassistant.components.zha # homeassistant.components.zha
zigpy==0.47.2 zigpy==0.47.3
# homeassistant.components.zoneminder # homeassistant.components.zoneminder
zm-py==0.5.2 zm-py==0.5.2

View File

@ -7,7 +7,7 @@
AEMET-OpenData==0.2.1 AEMET-OpenData==0.2.1
# homeassistant.components.aladdin_connect # homeassistant.components.aladdin_connect
AIOAladdinConnect==0.1.23 AIOAladdinConnect==0.1.25
# homeassistant.components.adax # homeassistant.components.adax
Adax-local==0.1.4 Adax-local==0.1.4
@ -314,7 +314,7 @@ bellows==0.31.1
bimmer_connected==0.9.6 bimmer_connected==0.9.6
# homeassistant.components.blebox # homeassistant.components.blebox
blebox_uniapi==2.0.0 blebox_uniapi==2.0.1
# homeassistant.components.blink # homeassistant.components.blink
blinkpy==0.19.0 blinkpy==0.19.0
@ -595,7 +595,7 @@ hole==0.7.0
holidays==0.14.2 holidays==0.14.2
# homeassistant.components.frontend # homeassistant.components.frontend
home-assistant-frontend==20220707.0 home-assistant-frontend==20220707.1
# homeassistant.components.home_connect # homeassistant.components.home_connect
homeconnect==0.7.1 homeconnect==0.7.1
@ -1101,7 +1101,7 @@ pylibrespot-java==0.1.0
pylitejet==0.3.0 pylitejet==0.3.0
# homeassistant.components.litterrobot # homeassistant.components.litterrobot
pylitterbot==2022.3.0 pylitterbot==2022.7.0
# homeassistant.components.lutron_caseta # homeassistant.components.lutron_caseta
pylutron-caseta==0.13.1 pylutron-caseta==0.13.1
@ -1331,7 +1331,7 @@ pytrafikverket==0.2.0.1
pyudev==0.22.0 pyudev==0.22.0
# homeassistant.components.unifiprotect # homeassistant.components.unifiprotect
pyunifiprotect==4.0.9 pyunifiprotect==4.0.10
# homeassistant.components.uptimerobot # homeassistant.components.uptimerobot
pyuptimerobot==22.2.0 pyuptimerobot==22.2.0
@ -1680,7 +1680,7 @@ zigpy-zigate==0.9.0
zigpy-znp==0.8.1 zigpy-znp==0.8.1
# homeassistant.components.zha # homeassistant.components.zha
zigpy==0.47.2 zigpy==0.47.3
# homeassistant.components.zwave_js # homeassistant.components.zwave_js
zwave-js-server-python==0.39.0 zwave-js-server-python==0.39.0

View File

@ -136,6 +136,10 @@ pydantic!=1.9.1
# Breaks asyncio # Breaks asyncio
# https://github.com/pubnub/python/issues/130 # https://github.com/pubnub/python/issues/130
pubnub!=6.4.0 pubnub!=6.4.0
# Package's __init__.pyi stub has invalid syntax and breaks mypy
# https://github.com/dahlia/iso4217/issues/16
iso4217!=1.10.20220401
""" """
IGNORE_PRE_COMMIT_HOOK_ID = ( IGNORE_PRE_COMMIT_HOOK_ID = (

View File

@ -346,7 +346,11 @@ async def test_doorbell_event(hass, aioclient_mock):
hass.states.async_set( hass.states.async_set(
"binary_sensor.test_doorbell", "binary_sensor.test_doorbell",
"off", "off",
{"friendly_name": "Test Doorbell Sensor", "device_class": "occupancy"}, {
"friendly_name": "Test Doorbell Sensor",
"device_class": "occupancy",
"linkquality": 42,
},
) )
await state_report.async_enable_proactive_mode(hass, get_default_config(hass)) await state_report.async_enable_proactive_mode(hass, get_default_config(hass))
@ -354,7 +358,21 @@ async def test_doorbell_event(hass, aioclient_mock):
hass.states.async_set( hass.states.async_set(
"binary_sensor.test_doorbell", "binary_sensor.test_doorbell",
"on", "on",
{"friendly_name": "Test Doorbell Sensor", "device_class": "occupancy"}, {
"friendly_name": "Test Doorbell Sensor",
"device_class": "occupancy",
"linkquality": 42,
},
)
hass.states.async_set(
"binary_sensor.test_doorbell",
"on",
{
"friendly_name": "Test Doorbell Sensor",
"device_class": "occupancy",
"linkquality": 99,
},
) )
# To trigger event listener # To trigger event listener
@ -386,6 +404,34 @@ async def test_doorbell_event(hass, aioclient_mock):
assert len(aioclient_mock.mock_calls) == 2 assert len(aioclient_mock.mock_calls) == 2
async def test_doorbell_event_from_unknown(hass, aioclient_mock):
"""Test doorbell press reports."""
aioclient_mock.post(TEST_URL, text="", status=202)
await state_report.async_enable_proactive_mode(hass, get_default_config(hass))
hass.states.async_set(
"binary_sensor.test_doorbell",
"on",
{
"friendly_name": "Test Doorbell Sensor",
"device_class": "occupancy",
},
)
# To trigger event listener
await hass.async_block_till_done()
assert len(aioclient_mock.mock_calls) == 1
call = aioclient_mock.mock_calls
call_json = call[0][2]
assert call_json["event"]["header"]["namespace"] == "Alexa.DoorbellEventSource"
assert call_json["event"]["header"]["name"] == "DoorbellPress"
assert call_json["event"]["payload"]["cause"]["type"] == "PHYSICAL_INTERACTION"
assert call_json["event"]["endpoint"]["endpointId"] == "binary_sensor#test_doorbell"
async def test_doorbell_event_fail(hass, aioclient_mock, caplog): async def test_doorbell_event_fail(hass, aioclient_mock, caplog):
"""Test proactive state retries once.""" """Test proactive state retries once."""
aioclient_mock.post( aioclient_mock.post(

View File

@ -7,6 +7,7 @@ import pytest
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_BRIGHTNESS,
ATTR_EFFECT,
ATTR_RGBW_COLOR, ATTR_RGBW_COLOR,
ATTR_SUPPORTED_COLOR_MODES, ATTR_SUPPORTED_COLOR_MODES,
ColorMode, ColorMode,
@ -509,17 +510,63 @@ async def test_turn_on_failure(feature, hass, config, caplog):
caplog.set_level(logging.ERROR) caplog.set_level(logging.ERROR)
feature_mock, entity_id = feature feature_mock, entity_id = feature
feature_mock.async_on = AsyncMock(side_effect=blebox_uniapi.error.BadOnValueError) feature_mock.async_on = AsyncMock(side_effect=ValueError)
await async_setup_entity(hass, config, entity_id) await async_setup_entity(hass, config, entity_id)
feature_mock.sensible_on_value = 123 feature_mock.sensible_on_value = 123
with pytest.raises(ValueError) as info:
await hass.services.async_call(
"light",
SERVICE_TURN_ON,
{"entity_id": entity_id},
blocking=True,
)
assert f"Turning on '{feature_mock.full_name}' failed: Bad value 123" in str(
info.value
)
async def test_wlightbox_on_effect(wlightbox, hass, config):
"""Test light on."""
feature_mock, entity_id = wlightbox
def initial_update():
feature_mock.is_on = False
feature_mock.async_update = AsyncMock(side_effect=initial_update)
await async_setup_entity(hass, config, entity_id)
feature_mock.async_update = AsyncMock()
state = hass.states.get(entity_id)
assert state.state == STATE_OFF
def turn_on(value):
feature_mock.is_on = True
feature_mock.effect = "POLICE"
feature_mock.async_on = AsyncMock(side_effect=turn_on)
with pytest.raises(ValueError) as info:
await hass.services.async_call(
"light",
SERVICE_TURN_ON,
{"entity_id": entity_id, ATTR_EFFECT: "NOT IN LIST"},
blocking=True,
)
assert (
f"Turning on with effect '{feature_mock.full_name}' failed: NOT IN LIST not in effect list."
in str(info.value)
)
await hass.services.async_call( await hass.services.async_call(
"light", "light",
SERVICE_TURN_ON, SERVICE_TURN_ON,
{"entity_id": entity_id}, {"entity_id": entity_id, ATTR_EFFECT: "POLICE"},
blocking=True, blocking=True,
) )
assert ( state = hass.states.get(entity_id)
f"Turning on '{feature_mock.full_name}' failed: Bad value 123 ()" in caplog.text assert state.attributes[ATTR_EFFECT] == "POLICE"
)

View File

@ -749,7 +749,9 @@ async def test_durations(hass, worker_finished_stream):
}, },
) )
source = generate_h264_video(duration=SEGMENT_DURATION + 1) source = generate_h264_video(
duration=round(SEGMENT_DURATION + target_part_duration + 1)
)
worker_finished, mock_stream = worker_finished_stream worker_finished, mock_stream = worker_finished_stream
with patch("homeassistant.components.stream.Stream", wraps=mock_stream): with patch("homeassistant.components.stream.Stream", wraps=mock_stream):