diff --git a/homeassistant/components/amcrest/__init__.py b/homeassistant/components/amcrest/__init__.py index 166d2e74ffd..f2472575259 100644 --- a/homeassistant/components/amcrest/__init__.py +++ b/homeassistant/components/amcrest/__init__.py @@ -52,6 +52,7 @@ from .const import ( DATA_AMCREST, DEVICES, DOMAIN, + RESOLUTION_LIST, SERVICE_EVENT, SERVICE_UPDATE, ) @@ -76,8 +77,6 @@ RECHECK_INTERVAL = timedelta(minutes=1) NOTIFICATION_ID = "amcrest_notification" NOTIFICATION_TITLE = "Amcrest Camera Setup" -RESOLUTION_LIST = {"high": 0, "low": 1} - SCAN_INTERVAL = timedelta(seconds=10) AUTHENTICATION_LIST = {"basic": "basic"} diff --git a/homeassistant/components/amcrest/camera.py b/homeassistant/components/amcrest/camera.py index 3846f4945a9..61b97b041cd 100644 --- a/homeassistant/components/amcrest/camera.py +++ b/homeassistant/components/amcrest/camera.py @@ -35,6 +35,7 @@ from .const import ( DATA_AMCREST, DEVICES, DOMAIN, + RESOLUTION_TO_STREAM, SERVICE_UPDATE, SNAPSHOT_TIMEOUT, ) @@ -533,13 +534,14 @@ class AmcrestCam(Camera): return async def _async_get_video(self) -> bool: - stream = {0: "Main", 1: "Extra"} return await self._api.async_is_video_enabled( - channel=0, stream=stream[self._resolution] + channel=0, stream=RESOLUTION_TO_STREAM[self._resolution] ) async def _async_set_video(self, enable: bool) -> None: - await self._api.async_set_video_enabled(enable, channel=0) + await self._api.async_set_video_enabled( + enable, channel=0, stream=RESOLUTION_TO_STREAM[self._resolution] + ) async def _async_enable_video(self, enable: bool) -> None: """Enable or disable camera video stream.""" @@ -548,7 +550,7 @@ class AmcrestCam(Camera): # recording on if video stream is being turned off. if self.is_recording and not enable: await self._async_enable_recording(False) - await self._async_change_setting(enable, "video", "is_streaming") + await self._async_change_setting(enable, "video", "_attr_is_streaming") if self._control_light: await self._async_change_light() @@ -585,10 +587,14 @@ class AmcrestCam(Camera): ) async def _async_get_audio(self) -> bool: - return await self._api.async_audio_enabled + return await self._api.async_is_audio_enabled( + channel=0, stream=RESOLUTION_TO_STREAM[self._resolution] + ) async def _async_set_audio(self, enable: bool) -> None: - await self._api.async_set_audio_enabled(enable) + await self._api.async_set_audio_enabled( + enable, channel=0, stream=RESOLUTION_TO_STREAM[self._resolution] + ) async def _async_enable_audio(self, enable: bool) -> None: """Enable or disable audio stream.""" diff --git a/homeassistant/components/amcrest/const.py b/homeassistant/components/amcrest/const.py index 89cde63a08a..6c2fe431d43 100644 --- a/homeassistant/components/amcrest/const.py +++ b/homeassistant/components/amcrest/const.py @@ -13,3 +13,6 @@ SNAPSHOT_TIMEOUT = 20 SERVICE_EVENT = "event" SERVICE_UPDATE = "update" + +RESOLUTION_LIST = {"high": 0, "low": 1} +RESOLUTION_TO_STREAM = {0: "Main", 1: "Extra"} diff --git a/homeassistant/components/amcrest/manifest.json b/homeassistant/components/amcrest/manifest.json index 5a7bec89e31..b4646be5e66 100644 --- a/homeassistant/components/amcrest/manifest.json +++ b/homeassistant/components/amcrest/manifest.json @@ -2,7 +2,7 @@ "domain": "amcrest", "name": "Amcrest", "documentation": "https://www.home-assistant.io/integrations/amcrest", - "requirements": ["amcrest==1.9.4"], + "requirements": ["amcrest==1.9.7"], "dependencies": ["ffmpeg"], "codeowners": ["@flacjacket"], "iot_class": "local_polling", diff --git a/homeassistant/components/doorbird/config_flow.py b/homeassistant/components/doorbird/config_flow.py index 31ddd1f6193..cc882b0ed50 100644 --- a/homeassistant/components/doorbird/config_flow.py +++ b/homeassistant/components/doorbird/config_flow.py @@ -12,7 +12,7 @@ from homeassistant.components import zeroconf from homeassistant.const import CONF_HOST, CONF_NAME, CONF_PASSWORD, CONF_USERNAME from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult -from homeassistant.util.network import is_link_local +from homeassistant.util.network import is_ipv4_address, is_link_local from .const import CONF_EVENTS, DOMAIN, DOORBIRD_OUI from .util import get_mac_address_from_doorstation_info @@ -103,6 +103,8 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_abort(reason="not_doorbird_device") if is_link_local(ip_address(host)): return self.async_abort(reason="link_local_address") + if not is_ipv4_address(host): + return self.async_abort(reason="not_ipv4_address") await self.async_set_unique_id(macaddress) self._abort_if_unique_id_configured(updates={CONF_HOST: host}) diff --git a/homeassistant/components/doorbird/translations/en.json b/homeassistant/components/doorbird/translations/en.json index db1cea2d73f..c67658196c4 100644 --- a/homeassistant/components/doorbird/translations/en.json +++ b/homeassistant/components/doorbird/translations/en.json @@ -3,7 +3,8 @@ "abort": { "already_configured": "Device is already configured", "link_local_address": "Link local addresses are not supported", - "not_doorbird_device": "This device is not a DoorBird" + "not_doorbird_device": "This device is not a DoorBird", + "not_ipv4_address": "Only IPv4 addresess are supported" }, "error": { "cannot_connect": "Failed to connect", diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index baf61343040..bcbf5b81ff8 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -3,7 +3,7 @@ "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", "requirements": [ - "home-assistant-frontend==20220301.1" + "home-assistant-frontend==20220301.2" ], "dependencies": [ "api", diff --git a/homeassistant/components/home_connect/manifest.json b/homeassistant/components/home_connect/manifest.json index 5667d539902..784042f884c 100644 --- a/homeassistant/components/home_connect/manifest.json +++ b/homeassistant/components/home_connect/manifest.json @@ -4,7 +4,7 @@ "documentation": "https://www.home-assistant.io/integrations/home_connect", "dependencies": ["http"], "codeowners": ["@DavidMStraub"], - "requirements": ["homeconnect==0.6.3"], + "requirements": ["homeconnect==0.7.0"], "config_flow": true, "iot_class": "cloud_push", "loggers": ["homeconnect"] diff --git a/homeassistant/components/isy994/manifest.json b/homeassistant/components/isy994/manifest.json index fe0fa9720ca..89e66533913 100644 --- a/homeassistant/components/isy994/manifest.json +++ b/homeassistant/components/isy994/manifest.json @@ -2,7 +2,7 @@ "domain": "isy994", "name": "Universal Devices ISY994", "documentation": "https://www.home-assistant.io/integrations/isy994", - "requirements": ["pyisy==3.0.1"], + "requirements": ["pyisy==3.0.5"], "codeowners": ["@bdraco", "@shbatm"], "config_flow": true, "ssdp": [ diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 53a2544611a..1bb318f11c5 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -135,6 +135,13 @@ DEFAULT_KEEPALIVE = 60 DEFAULT_PROTOCOL = PROTOCOL_311 DEFAULT_TLS_PROTOCOL = "auto" +DEFAULT_VALUES = { + CONF_PORT: DEFAULT_PORT, + CONF_WILL_MESSAGE: DEFAULT_WILL, + CONF_BIRTH_MESSAGE: DEFAULT_BIRTH, + CONF_DISCOVERY: DEFAULT_DISCOVERY, +} + ATTR_TOPIC_TEMPLATE = "topic_template" ATTR_PAYLOAD_TEMPLATE = "payload_template" @@ -190,7 +197,7 @@ CONFIG_SCHEMA_BASE = vol.Schema( vol.Coerce(int), vol.Range(min=15) ), vol.Optional(CONF_BROKER): cv.string, - vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, + vol.Optional(CONF_PORT): cv.port, vol.Optional(CONF_USERNAME): cv.string, vol.Optional(CONF_PASSWORD): cv.string, vol.Optional(CONF_CERTIFICATE): vol.Any("auto", cv.isfile), @@ -207,9 +214,9 @@ CONFIG_SCHEMA_BASE = vol.Schema( vol.Optional(CONF_PROTOCOL, default=DEFAULT_PROTOCOL): vol.All( cv.string, vol.In([PROTOCOL_31, PROTOCOL_311]) ), - vol.Optional(CONF_WILL_MESSAGE, default=DEFAULT_WILL): MQTT_WILL_BIRTH_SCHEMA, - vol.Optional(CONF_BIRTH_MESSAGE, default=DEFAULT_BIRTH): MQTT_WILL_BIRTH_SCHEMA, - vol.Optional(CONF_DISCOVERY, default=DEFAULT_DISCOVERY): cv.boolean, + vol.Optional(CONF_WILL_MESSAGE): MQTT_WILL_BIRTH_SCHEMA, + vol.Optional(CONF_BIRTH_MESSAGE): MQTT_WILL_BIRTH_SCHEMA, + vol.Optional(CONF_DISCOVERY): cv.boolean, # discovery_prefix must be a valid publish topic because if no # state topic is specified, it will be created with the given prefix. vol.Optional( @@ -613,6 +620,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: def _merge_config(entry, conf): """Merge configuration.yaml config with config entry.""" + # Base config on default values + conf = {**DEFAULT_VALUES, **conf} return {**conf, **entry.data} @@ -632,6 +641,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: override, ) + # Merge the configuration values from configuration.yaml conf = _merge_config(entry, conf) hass.data[DATA_MQTT] = MQTT( diff --git a/homeassistant/components/samsungtv/bridge.py b/homeassistant/components/samsungtv/bridge.py index 74daf1d34e0..481d3588bb7 100644 --- a/homeassistant/components/samsungtv/bridge.py +++ b/homeassistant/components/samsungtv/bridge.py @@ -10,7 +10,7 @@ from samsungctl import Remote from samsungctl.exceptions import AccessDenied, ConnectionClosed, UnhandledResponse from samsungtvws import SamsungTVWS from samsungtvws.exceptions import ConnectionFailure, HttpApiError -from websocket import WebSocketException +from websocket import WebSocketException, WebSocketTimeoutException from homeassistant.const import ( CONF_HOST, @@ -318,8 +318,8 @@ class SamsungTVWSBridge(SamsungTVBridge): def _get_app_list(self) -> dict[str, str] | None: """Get installed app list.""" - if self._app_list is None: - if remote := self._get_remote(): + if self._app_list is None and (remote := self._get_remote()): + with contextlib.suppress(WebSocketTimeoutException): raw_app_list: list[dict[str, str]] = remote.app_list() self._app_list = { app["name"]: app["appId"] diff --git a/homeassistant/components/shelly/sensor.py b/homeassistant/components/shelly/sensor.py index 21a7447e2b2..7e19b9724d7 100644 --- a/homeassistant/components/shelly/sensor.py +++ b/homeassistant/components/shelly/sensor.py @@ -174,6 +174,7 @@ SENSORS: Final = { value=lambda value: round(value / 1000, 2), device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, + available=lambda block: cast(int, block.energy) != -1, ), ("emeter", "energyReturned"): BlockSensorDescription( key="emeter|energyReturned", @@ -182,6 +183,7 @@ SENSORS: Final = { value=lambda value: round(value / 1000, 2), device_class=SensorDeviceClass.ENERGY, state_class=SensorStateClass.TOTAL_INCREASING, + available=lambda block: cast(int, block.energyReturned) != -1, ), ("light", "energy"): BlockSensorDescription( key="light|energy", diff --git a/homeassistant/components/somfy_mylink/cover.py b/homeassistant/components/somfy_mylink/cover.py index 7ac0aace47f..d1b09175deb 100644 --- a/homeassistant/components/somfy_mylink/cover.py +++ b/homeassistant/components/somfy_mylink/cover.py @@ -79,6 +79,7 @@ class SomfyShade(RestoreEntity, CoverEntity): self._attr_unique_id = target_id self._attr_name = name self._reverse = reverse + self._attr_is_closed = None self._attr_device_class = device_class self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, self._target_id)}, diff --git a/homeassistant/const.py b/homeassistant/const.py index 9f3d5e1c0f7..36f5c21b279 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -7,7 +7,7 @@ from .backports.enum import StrEnum MAJOR_VERSION: Final = 2022 MINOR_VERSION: Final = 3 -PATCH_VERSION: Final = "4" +PATCH_VERSION: Final = "5" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 2a441dffc35..20b3c5badd3 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -14,7 +14,7 @@ certifi>=2021.5.30 ciso8601==2.2.0 cryptography==35.0.0 hass-nabucasa==0.54.0 -home-assistant-frontend==20220301.1 +home-assistant-frontend==20220301.2 httpx==0.21.3 ifaddr==0.1.7 jinja2==3.0.3 diff --git a/requirements_all.txt b/requirements_all.txt index 8246fd24dbb..565cdcae1a3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -311,7 +311,7 @@ amberelectric==1.0.3 ambiclimate==0.2.1 # homeassistant.components.amcrest -amcrest==1.9.4 +amcrest==1.9.7 # homeassistant.components.androidtv androidtv[async]==0.0.63 @@ -843,13 +843,13 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220301.1 +home-assistant-frontend==20220301.2 # homeassistant.components.zwave # homeassistant-pyozw==0.1.10 # homeassistant.components.home_connect -homeconnect==0.6.3 +homeconnect==0.7.0 # homeassistant.components.homematicip_cloud homematicip==1.0.2 @@ -1610,7 +1610,7 @@ pyirishrail==0.0.2 pyiss==1.0.1 # homeassistant.components.isy994 -pyisy==3.0.1 +pyisy==3.0.5 # homeassistant.components.itach pyitachip2ir==0.0.7 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 29a9b6e8088..0ec2c864424 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -553,13 +553,13 @@ hole==0.7.0 holidays==0.13 # homeassistant.components.frontend -home-assistant-frontend==20220301.1 +home-assistant-frontend==20220301.2 # homeassistant.components.zwave # homeassistant-pyozw==0.1.10 # homeassistant.components.home_connect -homeconnect==0.6.3 +homeconnect==0.7.0 # homeassistant.components.homematicip_cloud homematicip==1.0.2 @@ -1015,7 +1015,7 @@ pyiqvia==2021.11.0 pyiss==1.0.1 # homeassistant.components.isy994 -pyisy==3.0.1 +pyisy==3.0.5 # homeassistant.components.kira pykira==0.1.1 diff --git a/setup.cfg b/setup.cfg index 5cdcab49caf..5e229a0da9d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = homeassistant -version = 2022.3.4 +version = 2022.3.5 author = The Home Assistant Authors author_email = hello@home-assistant.io license = Apache-2.0 diff --git a/tests/components/doorbird/test_config_flow.py b/tests/components/doorbird/test_config_flow.py index 10cc4a4fb77..cbf455653ff 100644 --- a/tests/components/doorbird/test_config_flow.py +++ b/tests/components/doorbird/test_config_flow.py @@ -116,6 +116,54 @@ async def test_form_zeroconf_link_local_ignored(hass): assert result["reason"] == "link_local_address" +async def test_form_zeroconf_ipv4_address(hass): + """Test we abort and update the ip address from zeroconf with an ipv4 address.""" + + config_entry = MockConfigEntry( + domain=DOMAIN, + unique_id="1CCAE3AAAAAA", + data=VALID_CONFIG, + options={CONF_EVENTS: ["event1", "event2", "event3"]}, + ) + config_entry.add_to_hass(hass) + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_ZEROCONF}, + data=zeroconf.ZeroconfServiceInfo( + host="4.4.4.4", + addresses=["4.4.4.4"], + hostname="mock_hostname", + name="Doorstation - abc123._axis-video._tcp.local.", + port=None, + properties={"macaddress": "1CCAE3AAAAAA"}, + type="mock_type", + ), + ) + assert result["type"] == "abort" + assert result["reason"] == "already_configured" + assert config_entry.data[CONF_HOST] == "4.4.4.4" + + +async def test_form_zeroconf_non_ipv4_ignored(hass): + """Test we abort when we get a non ipv4 address via zeroconf.""" + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_ZEROCONF}, + data=zeroconf.ZeroconfServiceInfo( + host="fd00::b27c:63bb:cc85:4ea0", + addresses=["fd00::b27c:63bb:cc85:4ea0"], + hostname="mock_hostname", + name="Doorstation - abc123._axis-video._tcp.local.", + port=None, + properties={"macaddress": "1CCAE3DOORBIRD"}, + type="mock_type", + ), + ) + assert result["type"] == "abort" + assert result["reason"] == "not_ipv4_address" + + async def test_form_zeroconf_correct_oui(hass): """Test we can setup from zeroconf with the correct OUI source.""" doorbirdapi = _get_mock_doorbirdapi_return_values( diff --git a/tests/components/efergy/__init__.py b/tests/components/efergy/__init__.py index ddddc56f4e4..5d77acc6838 100644 --- a/tests/components/efergy/__init__.py +++ b/tests/components/efergy/__init__.py @@ -1,12 +1,11 @@ """Tests for Efergy integration.""" from unittest.mock import AsyncMock, patch -from pyefergy import Efergy, exceptions +from pyefergy import exceptions from homeassistant.components.efergy import DOMAIN from homeassistant.const import CONF_API_KEY from homeassistant.core import HomeAssistant -from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.setup import async_setup_component from tests.common import MockConfigEntry, load_fixture @@ -56,10 +55,6 @@ async def mock_responses( ): """Mock responses from Efergy.""" base_url = "https://engage.efergy.com/mobile_proxy/" - api = Efergy( - token, session=async_get_clientsession(hass), utc_offset="America/New_York" - ) - assert api._utc_offset == 300 if error: aioclient_mock.get( f"{base_url}getInstant?token={token}",