From 4fbe7130791c2c7145f6d5052a67062011558ccb Mon Sep 17 00:00:00 2001 From: Maciej Bieniek Date: Sun, 8 Aug 2021 05:55:01 +0200 Subject: [PATCH 01/11] Add missing `motor_speed` sensor for Xiaomi Miio humidifier CA1 and CB1 (#54202) --- .../components/xiaomi_miio/sensor.py | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/xiaomi_miio/sensor.py b/homeassistant/components/xiaomi_miio/sensor.py index 413971aa880..9804e0298bc 100644 --- a/homeassistant/components/xiaomi_miio/sensor.py +++ b/homeassistant/components/xiaomi_miio/sensor.py @@ -45,6 +45,8 @@ from .const import ( DOMAIN, KEY_COORDINATOR, KEY_DEVICE, + MODEL_AIRHUMIDIFIER_CA1, + MODEL_AIRHUMIDIFIER_CB1, MODELS_HUMIDIFIER_MIOT, ) from .device import XiaomiCoordinatedMiioEntity, XiaomiMiioEntity @@ -63,16 +65,17 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( } ) -ATTR_POWER = "power" +ATTR_ACTUAL_SPEED = "actual_speed" ATTR_CHARGING = "charging" ATTR_DISPLAY_CLOCK = "display_clock" +ATTR_HUMIDITY = "humidity" +ATTR_MOTOR_SPEED = "motor_speed" ATTR_NIGHT_MODE = "night_mode" ATTR_NIGHT_TIME_BEGIN = "night_time_begin" ATTR_NIGHT_TIME_END = "night_time_end" +ATTR_POWER = "power" ATTR_SENSOR_STATE = "sensor_state" ATTR_WATER_LEVEL = "water_level" -ATTR_HUMIDITY = "humidity" -ATTR_ACTUAL_MOTOR_SPEED = "actual_speed" @dataclass @@ -121,6 +124,13 @@ SENSOR_TYPES = { valid_min_value=200.0, valid_max_value=2000.0, ), + "motor_speed": SensorType( + unit="rpm", + icon="mdi:fast-forward", + state_class=STATE_CLASS_MEASUREMENT, + valid_min_value=200.0, + valid_max_value=2000.0, + ), } HUMIDIFIER_SENSORS = { @@ -128,11 +138,17 @@ HUMIDIFIER_SENSORS = { ATTR_TEMPERATURE: "temperature", } +HUMIDIFIER_CA1_CB1_SENSORS = { + ATTR_HUMIDITY: "humidity", + ATTR_TEMPERATURE: "temperature", + ATTR_MOTOR_SPEED: "motor_speed", +} + HUMIDIFIER_SENSORS_MIOT = { ATTR_HUMIDITY: "humidity", ATTR_TEMPERATURE: "temperature", ATTR_WATER_LEVEL: "water_level", - ATTR_ACTUAL_MOTOR_SPEED: "actual_speed", + ATTR_ACTUAL_SPEED: "actual_speed", } @@ -191,11 +207,12 @@ async def async_setup_entry(hass, config_entry, async_add_entities): sensors = [] if model in MODELS_HUMIDIFIER_MIOT: device = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE] - coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR] sensors = HUMIDIFIER_SENSORS_MIOT + elif model in (MODEL_AIRHUMIDIFIER_CA1, MODEL_AIRHUMIDIFIER_CB1): + device = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE] + sensors = HUMIDIFIER_CA1_CB1_SENSORS elif model.startswith("zhimi.humidifier."): device = hass.data[DOMAIN][config_entry.entry_id][KEY_DEVICE] - coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR] sensors = HUMIDIFIER_SENSORS else: unique_id = config_entry.unique_id From 1934159fd0f9ab3bf8e0eb43ed43b865533c9115 Mon Sep 17 00:00:00 2001 From: Trinnik Date: Sat, 7 Aug 2021 21:51:05 -0600 Subject: [PATCH 02/11] Fix update entity prior to adding (#54015) --- homeassistant/components/aladdin_connect/cover.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/aladdin_connect/cover.py b/homeassistant/components/aladdin_connect/cover.py index 85f89f3043b..14e2b2f0ce2 100644 --- a/homeassistant/components/aladdin_connect/cover.py +++ b/homeassistant/components/aladdin_connect/cover.py @@ -49,7 +49,10 @@ def setup_platform( try: if not acc.login(): raise ValueError("Username or Password is incorrect") - add_entities(AladdinDevice(acc, door) for door in acc.get_doors()) + add_entities( + (AladdinDevice(acc, door) for door in acc.get_doors()), + update_before_add = True + ) except (TypeError, KeyError, NameError, ValueError) as ex: _LOGGER.error("%s", ex) hass.components.persistent_notification.create( From 3a17e22982e4b26dfe3e8a1c01656ab94b70896a Mon Sep 17 00:00:00 2001 From: Robert Hillis Date: Sat, 7 Aug 2021 01:18:08 -0400 Subject: [PATCH 03/11] Fix androidtv media_image_hash (#54188) --- homeassistant/components/androidtv/media_player.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/androidtv/media_player.py b/homeassistant/components/androidtv/media_player.py index 98d1ac0ae18..4d87ebc2592 100644 --- a/homeassistant/components/androidtv/media_player.py +++ b/homeassistant/components/androidtv/media_player.py @@ -449,6 +449,11 @@ class ADBDevice(MediaPlayerEntity): ATTR_HDMI_INPUT: None, } + @property + def media_image_hash(self): + """Hash value for media image.""" + return f"{datetime.now().timestamp()}" if self._screencap else None + @adb_decorator() async def _adb_screencap(self): """Take a screen capture from the device.""" @@ -458,9 +463,6 @@ class ADBDevice(MediaPlayerEntity): """Fetch current playing image.""" if not self._screencap or self.state in [STATE_OFF, None] or not self.available: return None, None - self._attr_media_image_hash = ( - f"{datetime.now().timestamp()}" if self._screencap else None - ) media_data = await self._adb_screencap() if media_data: From 94e26df6d373b828468d00ab2e72e0b02bde7fdf Mon Sep 17 00:00:00 2001 From: jan iversen Date: Sun, 8 Aug 2021 06:11:56 +0200 Subject: [PATCH 04/11] Solve missing automatic update of struct configuration in modbus (#54193) --- homeassistant/components/modbus/validators.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/modbus/validators.py b/homeassistant/components/modbus/validators.py index 9d72b611adc..f7fdae0a82a 100644 --- a/homeassistant/components/modbus/validators.py +++ b/homeassistant/components/modbus/validators.py @@ -72,6 +72,7 @@ def struct_validator(config): _LOGGER.warning(error) try: data_type = OLD_DATA_TYPES[data_type][config.get(CONF_COUNT, 1)] + config[CONF_DATA_TYPE] = data_type except KeyError as exp: error = f"{name} cannot convert automatically {data_type}" raise vol.Invalid(error) from exp From 56d0ef34fd83067a862de1477d9057d32b15b305 Mon Sep 17 00:00:00 2001 From: Mk4242 <76903406+Mk4242@users.noreply.github.com> Date: Sat, 7 Aug 2021 18:24:19 +0200 Subject: [PATCH 05/11] Update const.py (#54195) Remove extra attribute for FlowTemperature sensor, which prevents the ebusd integration from initialising --- homeassistant/components/ebusd/const.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/ebusd/const.py b/homeassistant/components/ebusd/const.py index 7052a9950fd..3d4ab508ca2 100644 --- a/homeassistant/components/ebusd/const.py +++ b/homeassistant/components/ebusd/const.py @@ -223,7 +223,6 @@ SENSOR_TYPES = { None, 4, DEVICE_CLASS_TEMPERATURE, - None, ], "Flame": ["Flame", None, "mdi:toggle-switch", 2, None], "PowerEnergyConsumptionHeatingCircuit": [ From 5dcf5edae0926e06a0713ad03674420f33a52b3e Mon Sep 17 00:00:00 2001 From: jan iversen Date: Sun, 8 Aug 2021 06:10:08 +0200 Subject: [PATCH 06/11] Add parameter to delay sending of requests in modbus (#54203) --- homeassistant/components/modbus/__init__.py | 2 ++ homeassistant/components/modbus/const.py | 1 + homeassistant/components/modbus/modbus.py | 11 +++++++++-- tests/components/modbus/test_init.py | 2 ++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/modbus/__init__.py b/homeassistant/components/modbus/__init__.py index 16be39230db..43aa49e6da7 100644 --- a/homeassistant/components/modbus/__init__.py +++ b/homeassistant/components/modbus/__init__.py @@ -66,6 +66,7 @@ from .const import ( CONF_INPUT_TYPE, CONF_MAX_TEMP, CONF_MIN_TEMP, + CONF_MSG_WAIT, CONF_PARITY, CONF_PRECISION, CONF_RETRIES, @@ -283,6 +284,7 @@ MODBUS_SCHEMA = vol.Schema( vol.Optional(CONF_DELAY, default=0): cv.positive_int, vol.Optional(CONF_RETRIES, default=3): cv.positive_int, vol.Optional(CONF_RETRY_ON_EMPTY, default=False): cv.boolean, + vol.Optional(CONF_MSG_WAIT): cv.positive_int, vol.Optional(CONF_BINARY_SENSORS): vol.All( cv.ensure_list, [BINARY_SENSOR_SCHEMA] ), diff --git a/homeassistant/components/modbus/const.py b/homeassistant/components/modbus/const.py index 49b7683435e..c5d182cdf4a 100644 --- a/homeassistant/components/modbus/const.py +++ b/homeassistant/components/modbus/const.py @@ -31,6 +31,7 @@ CONF_INPUTS = "inputs" CONF_INPUT_TYPE = "input_type" CONF_MAX_TEMP = "max_temp" CONF_MIN_TEMP = "min_temp" +CONF_MSG_WAIT = "message_wait_milliseconds" CONF_PARITY = "parity" CONF_REGISTER = "register" CONF_REGISTER_TYPE = "register_type" diff --git a/homeassistant/components/modbus/modbus.py b/homeassistant/components/modbus/modbus.py index 8d2ea46e293..1cb68aa4fd6 100644 --- a/homeassistant/components/modbus/modbus.py +++ b/homeassistant/components/modbus/modbus.py @@ -39,6 +39,7 @@ from .const import ( CONF_BAUDRATE, CONF_BYTESIZE, CONF_CLOSE_COMM_ON_ERROR, + CONF_MSG_WAIT, CONF_PARITY, CONF_RETRIES, CONF_RETRY_ON_EMPTY, @@ -229,6 +230,12 @@ class ModbusHub: self._pb_params["framer"] = ModbusRtuFramer Defaults.Timeout = client_config[CONF_TIMEOUT] + if CONF_MSG_WAIT in client_config: + self._msg_wait = client_config[CONF_MSG_WAIT] / 1000 + elif self._config_type == CONF_SERIAL: + self._msg_wait = 30 / 1000 + else: + self._msg_wait = 0 def _log_error(self, text: str, error_state=True): log_text = f"Pymodbus: {text}" @@ -322,7 +329,7 @@ class ModbusHub: result = await self.hass.async_add_executor_job( self._pymodbus_call, unit, address, value, use_call ) - if self._config_type == "serial": + if self._msg_wait: # small delay until next request/response - await asyncio.sleep(30 / 1000) + await asyncio.sleep(self._msg_wait) return result diff --git a/tests/components/modbus/test_init.py b/tests/components/modbus/test_init.py index 8b8d063bf02..b9f6420604f 100644 --- a/tests/components/modbus/test_init.py +++ b/tests/components/modbus/test_init.py @@ -40,6 +40,7 @@ from homeassistant.components.modbus.const import ( CONF_BYTESIZE, CONF_DATA_TYPE, CONF_INPUT_TYPE, + CONF_MSG_WAIT, CONF_PARITY, CONF_STOPBITS, CONF_SWAP, @@ -245,6 +246,7 @@ async def test_exception_struct_validator(do_config): CONF_PORT: "usb01", CONF_PARITY: "E", CONF_STOPBITS: 1, + CONF_MSG_WAIT: 100, }, { CONF_TYPE: "serial", From 13ded1e5b28befb491e61594c1efd1e4df09343a Mon Sep 17 00:00:00 2001 From: carstenschroeder Date: Sun, 8 Aug 2021 06:03:20 +0200 Subject: [PATCH 07/11] Bugfix: Bring back unique IDs for ADS covers after #52488 (#54212) --- homeassistant/components/ads/cover.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/ads/cover.py b/homeassistant/components/ads/cover.py index 0cd0264cb50..976bfd58fed 100644 --- a/homeassistant/components/ads/cover.py +++ b/homeassistant/components/ads/cover.py @@ -91,13 +91,13 @@ class AdsCover(AdsEntity, CoverEntity): ): """Initialize AdsCover entity.""" super().__init__(ads_hub, name, ads_var_is_closed) - if self._ads_var is None: + if self._attr_unique_id is None: if ads_var_position is not None: - self._unique_id = ads_var_position + self._attr_unique_id = ads_var_position elif ads_var_pos_set is not None: - self._unique_id = ads_var_pos_set + self._attr_unique_id = ads_var_pos_set elif ads_var_open is not None: - self._unique_id = ads_var_open + self._attr_unique_id = ads_var_open self._state_dict[STATE_KEY_POSITION] = None self._ads_var_position = ads_var_position From 724f11bb0d27815acea80f464d709ca3adf0bb4d Mon Sep 17 00:00:00 2001 From: Dermot Duffy Date: Sat, 7 Aug 2021 21:29:52 -0700 Subject: [PATCH 08/11] Don't block motionEye setup on NoURLAvailableError (#54225) Co-authored-by: Paulus Schoutsen --- .../components/motioneye/__init__.py | 68 +++++++++++-------- tests/components/motioneye/test_web_hooks.py | 31 +++++++++ 2 files changed, 71 insertions(+), 28 deletions(-) diff --git a/homeassistant/components/motioneye/__init__.py b/homeassistant/components/motioneye/__init__.py index 2ade7c48e1b..acafdceeb05 100644 --- a/homeassistant/components/motioneye/__init__.py +++ b/homeassistant/components/motioneye/__init__.py @@ -53,7 +53,7 @@ from homeassistant.helpers.dispatcher import ( async_dispatcher_send, ) from homeassistant.helpers.entity import DeviceInfo, EntityDescription -from homeassistant.helpers.network import get_url +from homeassistant.helpers.network import NoURLAvailableError, get_url from homeassistant.helpers.update_coordinator import ( CoordinatorEntity, DataUpdateCoordinator, @@ -145,12 +145,21 @@ def listen_for_new_cameras( @callback -def async_generate_motioneye_webhook(hass: HomeAssistant, webhook_id: str) -> str: +def async_generate_motioneye_webhook( + hass: HomeAssistant, webhook_id: str +) -> str | None: """Generate the full local URL for a webhook_id.""" - return "{}{}".format( - get_url(hass, allow_cloud=False), - async_generate_path(webhook_id), - ) + try: + return "{}{}".format( + get_url(hass, allow_cloud=False), + async_generate_path(webhook_id), + ) + except NoURLAvailableError: + _LOGGER.warning( + "Unable to get Home Assistant URL. Have you set the internal and/or " + "external URLs in Configuration -> General?" + ) + return None @callback @@ -228,28 +237,31 @@ def _add_camera( if entry.options.get(CONF_WEBHOOK_SET, DEFAULT_WEBHOOK_SET): url = async_generate_motioneye_webhook(hass, entry.data[CONF_WEBHOOK_ID]) - if _set_webhook( - _build_url( - device, - url, - EVENT_MOTION_DETECTED, - EVENT_MOTION_DETECTED_KEYS, - ), - KEY_WEB_HOOK_NOTIFICATIONS_URL, - KEY_WEB_HOOK_NOTIFICATIONS_HTTP_METHOD, - KEY_WEB_HOOK_NOTIFICATIONS_ENABLED, - camera, - ) | _set_webhook( - _build_url( - device, - url, - EVENT_FILE_STORED, - EVENT_FILE_STORED_KEYS, - ), - KEY_WEB_HOOK_STORAGE_URL, - KEY_WEB_HOOK_STORAGE_HTTP_METHOD, - KEY_WEB_HOOK_STORAGE_ENABLED, - camera, + if url and ( + _set_webhook( + _build_url( + device, + url, + EVENT_MOTION_DETECTED, + EVENT_MOTION_DETECTED_KEYS, + ), + KEY_WEB_HOOK_NOTIFICATIONS_URL, + KEY_WEB_HOOK_NOTIFICATIONS_HTTP_METHOD, + KEY_WEB_HOOK_NOTIFICATIONS_ENABLED, + camera, + ) + | _set_webhook( + _build_url( + device, + url, + EVENT_FILE_STORED, + EVENT_FILE_STORED_KEYS, + ), + KEY_WEB_HOOK_STORAGE_URL, + KEY_WEB_HOOK_STORAGE_HTTP_METHOD, + KEY_WEB_HOOK_STORAGE_ENABLED, + camera, + ) ): hass.async_create_task(client.async_set_camera(camera_id, camera)) diff --git a/tests/components/motioneye/test_web_hooks.py b/tests/components/motioneye/test_web_hooks.py index 03b4e8bc46a..f20ef5101e9 100644 --- a/tests/components/motioneye/test_web_hooks.py +++ b/tests/components/motioneye/test_web_hooks.py @@ -32,11 +32,13 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr +from homeassistant.helpers.network import NoURLAvailableError from homeassistant.setup import async_setup_component from . import ( TEST_CAMERA, TEST_CAMERA_DEVICE_IDENTIFIER, + TEST_CAMERA_ENTITY_ID, TEST_CAMERA_ID, TEST_CAMERA_NAME, TEST_CAMERAS, @@ -251,6 +253,35 @@ async def test_setup_camera_with_correct_webhook( assert not client.async_set_camera.called +async def test_setup_camera_with_no_home_assistant_urls( + hass: HomeAssistant, + caplog: Any, +) -> None: + """Verify setup works without Home Assistant internal/external URLs.""" + + client = create_mock_motioneye_client() + config_entry = create_mock_motioneye_config_entry(hass, data={CONF_URL: TEST_URL}) + + with patch( + "homeassistant.components.motioneye.get_url", side_effect=NoURLAvailableError + ): + await setup_mock_motioneye_config_entry( + hass, + config_entry=config_entry, + client=client, + ) + + # Should log a warning ... + assert "Unable to get Home Assistant URL" in caplog.text + + # ... should not set callbacks in the camera ... + assert not client.async_set_camera.called + + # ... but camera should still be present. + entity_state = hass.states.get(TEST_CAMERA_ENTITY_ID) + assert entity_state + + async def test_good_query(hass: HomeAssistant, aiohttp_client: Any) -> None: """Test good callbacks.""" await async_setup_component(hass, "http", {"http": {}}) From fb6aca4f8bfe15b471e04025af4d5ec96a02f49c Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sat, 7 Aug 2021 21:00:37 -0700 Subject: [PATCH 09/11] Pin google-cloud-pubsub to an older version (#54239) Pin google-cloud-pubsub to an older version, since newer versions have a pin that is incompatible with the existing grpcio pin already in package_constraints.txt --- homeassistant/package_constraints.txt | 5 +++++ script/gen_requirements_all.py | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 617a057743b..6d22aa51b24 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -51,6 +51,11 @@ httplib2>=0.19.0 # https://github.com/home-assistant/core/issues/40148 grpcio==1.31.0 +# Newer versions of cloud pubsub pin a higher version of grpcio. This can +# be reverted when the grpcio pin is reverted, see: +# https://github.com/home-assistant/core/issues/53427 +google-cloud-pubsub==2.1.0 + # This is a old unmaintained library and is replaced with pycryptodome pycrypto==1000000000.0.0 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 7dcc4f71fe8..934ea9be90c 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -73,6 +73,11 @@ httplib2>=0.19.0 # https://github.com/home-assistant/core/issues/40148 grpcio==1.31.0 +# Newer versions of cloud pubsub pin a higher version of grpcio. This can +# be reverted when the grpcio pin is reverted, see: +# https://github.com/home-assistant/core/issues/53427 +google-cloud-pubsub==2.1.0 + # This is a old unmaintained library and is replaced with pycryptodome pycrypto==1000000000.0.0 From f458f330a53c6144360793210ce32350c3cd2250 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 7 Aug 2021 21:34:56 -0700 Subject: [PATCH 10/11] Bumped version to 2021.8.4 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index d4a79d3a8bb..4c7ab9742b0 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -5,7 +5,7 @@ from typing import Final MAJOR_VERSION: Final = 2021 MINOR_VERSION: Final = 8 -PATCH_VERSION: Final = "3" +PATCH_VERSION: Final = "4" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0) From fe9808926e4bf10867f3f25d1e71e53ff8e4566a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 7 Aug 2021 21:10:21 -0700 Subject: [PATCH 11/11] Fix formatting (#54247) --- homeassistant/components/aladdin_connect/cover.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/aladdin_connect/cover.py b/homeassistant/components/aladdin_connect/cover.py index 14e2b2f0ce2..5cebe3622dc 100644 --- a/homeassistant/components/aladdin_connect/cover.py +++ b/homeassistant/components/aladdin_connect/cover.py @@ -51,7 +51,7 @@ def setup_platform( raise ValueError("Username or Password is incorrect") add_entities( (AladdinDevice(acc, door) for door in acc.get_doors()), - update_before_add = True + update_before_add=True, ) except (TypeError, KeyError, NameError, ValueError) as ex: _LOGGER.error("%s", ex)