From d670df74cb346805a173e8118bf97818396ac6ad Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Fri, 2 Sep 2022 18:54:20 +0200 Subject: [PATCH 01/34] Fix reload of MQTT config entries (#76089) * Wait for unsubscribes * Spelling comment * Remove notify_all() during _register_mid() * Update homeassistant/components/mqtt/client.py Co-authored-by: Erik Montnemery * Correct handling reload manual set up MQTT items * Save and restore device trigger subscriptions * Clarify we are storing all remaining subscriptions Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/__init__.py | 26 +++++++++++++++++--- homeassistant/components/mqtt/client.py | 10 +++++--- homeassistant/components/mqtt/const.py | 1 + homeassistant/components/mqtt/mixins.py | 7 ++++++ tests/components/mqtt/test_device_trigger.py | 20 ++++++++++++++- 5 files changed, 56 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 1121377a30e..3ec9a7e9d4e 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -71,6 +71,7 @@ from .const import ( # noqa: F401 DATA_MQTT_RELOAD_DISPATCHERS, DATA_MQTT_RELOAD_ENTRY, DATA_MQTT_RELOAD_NEEDED, + DATA_MQTT_SUBSCRIPTIONS_TO_RESTORE, DATA_MQTT_UPDATED_CONFIG, DEFAULT_ENCODING, DEFAULT_QOS, @@ -315,6 +316,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return False hass.data[DATA_MQTT] = MQTT(hass, entry, conf) + # Restore saved subscriptions + if DATA_MQTT_SUBSCRIPTIONS_TO_RESTORE in hass.data: + hass.data[DATA_MQTT].subscriptions = hass.data.pop( + DATA_MQTT_SUBSCRIPTIONS_TO_RESTORE + ) entry.add_update_listener(_async_config_entry_updated) await hass.data[DATA_MQTT].async_connect() @@ -438,6 +444,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_forward_entry_setup_and_setup_discovery(config_entry): """Forward the config entry setup to the platforms and set up discovery.""" + reload_manual_setup: bool = False # Local import to avoid circular dependencies # pylint: disable-next=import-outside-toplevel from . import device_automation, tag @@ -460,8 +467,17 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: await _async_setup_discovery(hass, conf, entry) # Setup reload service after all platforms have loaded await async_setup_reload_service() + # When the entry is reloaded, also reload manual set up items to enable MQTT + if DATA_MQTT_RELOAD_ENTRY in hass.data: + hass.data.pop(DATA_MQTT_RELOAD_ENTRY) + reload_manual_setup = True + + # When the entry was disabled before, reload manual set up items to enable MQTT again if DATA_MQTT_RELOAD_NEEDED in hass.data: hass.data.pop(DATA_MQTT_RELOAD_NEEDED) + reload_manual_setup = True + + if reload_manual_setup: await async_reload_manual_mqtt_items(hass) await async_forward_entry_setup_and_setup_discovery(entry) @@ -613,8 +629,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: mqtt_client.cleanup() # Trigger reload manual MQTT items at entry setup - # Reload the legacy yaml platform - await async_reload_integration_platforms(hass, DOMAIN, RELOADABLE_PLATFORMS) if (mqtt_entry_status := mqtt_config_entry_enabled(hass)) is False: # The entry is disabled reload legacy manual items when the entry is enabled again hass.data[DATA_MQTT_RELOAD_NEEDED] = True @@ -622,7 +636,13 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # The entry is reloaded: # Trigger re-fetching the yaml config at entry setup hass.data[DATA_MQTT_RELOAD_ENTRY] = True - # Stop the loop + # Reload the legacy yaml platform to make entities unavailable + await async_reload_integration_platforms(hass, DOMAIN, RELOADABLE_PLATFORMS) + # Wait for all ACKs and stop the loop await mqtt_client.async_disconnect() + # Store remaining subscriptions to be able to restore or reload them + # when the entry is set up again + if mqtt_client.subscriptions: + hass.data[DATA_MQTT_SUBSCRIPTIONS_TO_RESTORE] = mqtt_client.subscriptions return True diff --git a/homeassistant/components/mqtt/client.py b/homeassistant/components/mqtt/client.py index 192de624f17..57f51593ed4 100644 --- a/homeassistant/components/mqtt/client.py +++ b/homeassistant/components/mqtt/client.py @@ -309,7 +309,7 @@ class MQTT: def __init__( self, - hass: HomeAssistant, + hass, config_entry, conf, ) -> None: @@ -435,12 +435,13 @@ class MQTT: """Return False if there are unprocessed ACKs.""" return not bool(self._pending_operations) - # wait for ACK-s to be processesed (unsubscribe only) + # wait for ACKs to be processed async with self._pending_operations_condition: await self._pending_operations_condition.wait_for(no_more_acks) # stop the MQTT loop - await self.hass.async_add_executor_job(stop) + async with self._paho_lock: + await self.hass.async_add_executor_job(stop) async def async_subscribe( self, @@ -501,7 +502,8 @@ class MQTT: async with self._paho_lock: mid = await self.hass.async_add_executor_job(_client_unsubscribe, topic) await self._register_mid(mid) - self.hass.async_create_task(self._wait_for_mid(mid)) + + self.hass.async_create_task(self._wait_for_mid(mid)) async def _async_perform_subscriptions( self, subscriptions: Iterable[tuple[str, int]] diff --git a/homeassistant/components/mqtt/const.py b/homeassistant/components/mqtt/const.py index 6a5cb912fce..0c711e097d5 100644 --- a/homeassistant/components/mqtt/const.py +++ b/homeassistant/components/mqtt/const.py @@ -32,6 +32,7 @@ CONF_TLS_VERSION = "tls_version" CONFIG_ENTRY_IS_SETUP = "mqtt_config_entry_is_setup" DATA_MQTT = "mqtt" +DATA_MQTT_SUBSCRIPTIONS_TO_RESTORE = "mqtt_client_subscriptions" DATA_MQTT_CONFIG = "mqtt_config" MQTT_DATA_DEVICE_TRACKER_LEGACY = "mqtt_device_tracker_legacy" DATA_MQTT_RELOAD_DISPATCHERS = "mqtt_reload_dispatchers" diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index faef154dd84..75532f75c13 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -65,6 +65,7 @@ from .const import ( DATA_MQTT, DATA_MQTT_CONFIG, DATA_MQTT_RELOAD_DISPATCHERS, + DATA_MQTT_RELOAD_ENTRY, DATA_MQTT_UPDATED_CONFIG, DEFAULT_ENCODING, DEFAULT_PAYLOAD_AVAILABLE, @@ -363,6 +364,12 @@ async def async_setup_platform_helper( async_setup_entities: SetupEntity, ) -> None: """Help to set up the platform for manual configured MQTT entities.""" + if DATA_MQTT_RELOAD_ENTRY in hass.data: + _LOGGER.debug( + "MQTT integration is %s, skipping setup of manually configured MQTT items while unloading the config entry", + platform_domain, + ) + return if not (entry_status := mqtt_config_entry_enabled(hass)): _LOGGER.warning( "MQTT integration is %s, skipping setup of manually configured MQTT %s", diff --git a/tests/components/mqtt/test_device_trigger.py b/tests/components/mqtt/test_device_trigger.py index 8363ca34fd7..661075c3cbe 100644 --- a/tests/components/mqtt/test_device_trigger.py +++ b/tests/components/mqtt/test_device_trigger.py @@ -4,6 +4,7 @@ from unittest.mock import patch import pytest +from homeassistant import config as hass_config import homeassistant.components.automation as automation from homeassistant.components.device_automation import DeviceAutomationType from homeassistant.components.mqtt import _LOGGER, DOMAIN, debug_info @@ -1425,7 +1426,24 @@ async def test_unload_entry(hass, calls, device_reg, mqtt_mock, tmp_path) -> Non await help_test_unload_config_entry(hass, tmp_path, {}) - # Fake short press 2 + # Rediscover message and fake short press 2 (non impact) + async_fire_mqtt_message(hass, "homeassistant/device_automation/bla1/config", data1) + await hass.async_block_till_done() async_fire_mqtt_message(hass, "foobar/triggers/button1", "short_press") await hass.async_block_till_done() assert len(calls) == 1 + + mqtt_entry = hass.config_entries.async_entries("mqtt")[0] + + # Load the entry again + new_yaml_config_file = tmp_path / "configuration.yaml" + new_yaml_config_file.write_text("") + with patch.object(hass_config, "YAML_CONFIG_FILE", new_yaml_config_file): + await mqtt_entry.async_setup(hass) + + # Rediscover and fake short press 3 + async_fire_mqtt_message(hass, "homeassistant/device_automation/bla1/config", data1) + await hass.async_block_till_done() + async_fire_mqtt_message(hass, "foobar/triggers/button1", "short_press") + await hass.async_block_till_done() + assert len(calls) == 2 From 21f6b50f7c0d84cf18e57152d267779ff697e73b Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Fri, 9 Sep 2022 15:24:26 +0200 Subject: [PATCH 02/34] Clear MQTT discovery topic when a disabled entity is removed (#77757) * Cleanup discovery on entity removal * Add test * Cleanup and test * Test with clearing payload not unique id * Address comments * Tests cover and typing * Just pass hass * reuse code * Follow up comments revert changes to cover tests * Add test unique_id has priority over disabled * Update homeassistant/components/mqtt/__init__.py Co-authored-by: Erik Montnemery Co-authored-by: Erik Montnemery --- homeassistant/components/mqtt/__init__.py | 16 +- homeassistant/components/mqtt/const.py | 1 + homeassistant/components/mqtt/mixins.py | 51 ++++++- tests/components/mqtt/test_discovery.py | 170 ++++++++++++++++++++++ 4 files changed, 232 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 3ec9a7e9d4e..842e5b6405f 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -20,7 +20,13 @@ from homeassistant.const import ( CONF_USERNAME, SERVICE_RELOAD, ) -from homeassistant.core import HassJob, HomeAssistant, ServiceCall, callback +from homeassistant.core import ( + CALLBACK_TYPE, + HassJob, + HomeAssistant, + ServiceCall, + callback, +) from homeassistant.exceptions import TemplateError, Unauthorized from homeassistant.helpers import ( config_validation as cv, @@ -68,6 +74,7 @@ from .const import ( # noqa: F401 CONFIG_ENTRY_IS_SETUP, DATA_MQTT, DATA_MQTT_CONFIG, + DATA_MQTT_DISCOVERY_REGISTRY_HOOKS, DATA_MQTT_RELOAD_DISPATCHERS, DATA_MQTT_RELOAD_ENTRY, DATA_MQTT_RELOAD_NEEDED, @@ -315,6 +322,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # Bail out return False + hass.data[DATA_MQTT_DISCOVERY_REGISTRY_HOOKS] = {} hass.data[DATA_MQTT] = MQTT(hass, entry, conf) # Restore saved subscriptions if DATA_MQTT_SUBSCRIPTIONS_TO_RESTORE in hass.data: @@ -638,6 +646,12 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data[DATA_MQTT_RELOAD_ENTRY] = True # Reload the legacy yaml platform to make entities unavailable await async_reload_integration_platforms(hass, DOMAIN, RELOADABLE_PLATFORMS) + # Cleanup entity registry hooks + registry_hooks: dict[tuple, CALLBACK_TYPE] = hass.data[ + DATA_MQTT_DISCOVERY_REGISTRY_HOOKS + ] + while registry_hooks: + registry_hooks.popitem()[1]() # Wait for all ACKs and stop the loop await mqtt_client.async_disconnect() # Store remaining subscriptions to be able to restore or reload them diff --git a/homeassistant/components/mqtt/const.py b/homeassistant/components/mqtt/const.py index 0c711e097d5..c8af58862e0 100644 --- a/homeassistant/components/mqtt/const.py +++ b/homeassistant/components/mqtt/const.py @@ -33,6 +33,7 @@ CONF_TLS_VERSION = "tls_version" CONFIG_ENTRY_IS_SETUP = "mqtt_config_entry_is_setup" DATA_MQTT = "mqtt" DATA_MQTT_SUBSCRIPTIONS_TO_RESTORE = "mqtt_client_subscriptions" +DATA_MQTT_DISCOVERY_REGISTRY_HOOKS = "mqtt_discovery_registry_hooks" DATA_MQTT_CONFIG = "mqtt_config" MQTT_DATA_DEVICE_TRACKER_LEGACY = "mqtt_device_tracker_legacy" DATA_MQTT_RELOAD_DISPATCHERS = "mqtt_reload_dispatchers" diff --git a/homeassistant/components/mqtt/mixins.py b/homeassistant/components/mqtt/mixins.py index 75532f75c13..fddbe838303 100644 --- a/homeassistant/components/mqtt/mixins.py +++ b/homeassistant/components/mqtt/mixins.py @@ -28,7 +28,13 @@ from homeassistant.const import ( CONF_UNIQUE_ID, CONF_VALUE_TEMPLATE, ) -from homeassistant.core import Event, HomeAssistant, async_get_hass, callback +from homeassistant.core import ( + CALLBACK_TYPE, + Event, + HomeAssistant, + async_get_hass, + callback, +) from homeassistant.helpers import ( config_validation as cv, device_registry as dr, @@ -48,6 +54,7 @@ from homeassistant.helpers.entity import ( async_generate_entity_id, ) from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.event import async_track_entity_registry_updated_event from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.helpers.json import json_loads from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType @@ -64,6 +71,7 @@ from .const import ( CONF_TOPIC, DATA_MQTT, DATA_MQTT_CONFIG, + DATA_MQTT_DISCOVERY_REGISTRY_HOOKS, DATA_MQTT_RELOAD_DISPATCHERS, DATA_MQTT_RELOAD_ENTRY, DATA_MQTT_UPDATED_CONFIG, @@ -654,6 +662,17 @@ async def async_remove_discovery_payload(hass: HomeAssistant, discovery_data: di await async_publish(hass, discovery_topic, "", retain=True) +async def async_clear_discovery_topic_if_entity_removed( + hass: HomeAssistant, + discovery_data: dict[str, Any], + event: Event, +) -> None: + """Clear the discovery topic if the entity is removed.""" + if event.data["action"] == "remove": + # publish empty payload to config topic to avoid re-adding + await async_remove_discovery_payload(hass, discovery_data) + + class MqttDiscoveryDeviceUpdate: """Add support for auto discovery for platforms without an entity.""" @@ -787,7 +806,8 @@ class MqttDiscoveryUpdate(Entity): def __init__( self, - discovery_data: dict, + hass: HomeAssistant, + discovery_data: dict | None, discovery_update: Callable | None = None, ) -> None: """Initialize the discovery update mixin.""" @@ -795,6 +815,14 @@ class MqttDiscoveryUpdate(Entity): self._discovery_update = discovery_update self._remove_discovery_updated: Callable | None = None self._removed_from_hass = False + if discovery_data is None: + return + self._registry_hooks: dict[tuple, CALLBACK_TYPE] = hass.data[ + DATA_MQTT_DISCOVERY_REGISTRY_HOOKS + ] + discovery_hash: tuple[str, str] = discovery_data[ATTR_DISCOVERY_HASH] + if discovery_hash in self._registry_hooks: + self._registry_hooks.pop(discovery_hash)() async def async_added_to_hass(self) -> None: """Subscribe to discovery updates.""" @@ -857,7 +885,7 @@ class MqttDiscoveryUpdate(Entity): async def async_removed_from_registry(self) -> None: """Clear retained discovery topic in broker.""" - if not self._removed_from_hass: + if not self._removed_from_hass and self._discovery_data is not None: # Stop subscribing to discovery updates to not trigger when we clear the # discovery topic self._cleanup_discovery_on_remove() @@ -868,7 +896,20 @@ class MqttDiscoveryUpdate(Entity): @callback def add_to_platform_abort(self) -> None: """Abort adding an entity to a platform.""" - if self._discovery_data: + if self._discovery_data is not None: + discovery_hash: tuple = self._discovery_data[ATTR_DISCOVERY_HASH] + if self.registry_entry is not None: + self._registry_hooks[ + discovery_hash + ] = async_track_entity_registry_updated_event( + self.hass, + self.entity_id, + partial( + async_clear_discovery_topic_if_entity_removed, + self.hass, + self._discovery_data, + ), + ) stop_discovery_updates(self.hass, self._discovery_data) send_discovery_done(self.hass, self._discovery_data) super().add_to_platform_abort() @@ -976,7 +1017,7 @@ class MqttEntity( # Initialize mixin classes MqttAttributes.__init__(self, config) MqttAvailability.__init__(self, config) - MqttDiscoveryUpdate.__init__(self, discovery_data, self.discovery_update) + MqttDiscoveryUpdate.__init__(self, hass, discovery_data, self.discovery_update) MqttEntityDeviceInfo.__init__(self, config.get(CONF_DEVICE), config_entry) def _init_entity_id(self): diff --git a/tests/components/mqtt/test_discovery.py b/tests/components/mqtt/test_discovery.py index 29ca1f11743..c625d0a21f9 100644 --- a/tests/components/mqtt/test_discovery.py +++ b/tests/components/mqtt/test_discovery.py @@ -1,4 +1,5 @@ """The tests for the MQTT discovery.""" +import copy import json from pathlib import Path import re @@ -23,6 +24,8 @@ from homeassistant.const import ( import homeassistant.core as ha from homeassistant.setup import async_setup_component +from .test_common import help_test_unload_config_entry + from tests.common import ( MockConfigEntry, async_capture_events, @@ -1356,3 +1359,170 @@ async def test_mqtt_discovery_unsubscribe_once( await hass.async_block_till_done() await hass.async_block_till_done() mqtt_client_mock.unsubscribe.assert_called_once_with("comp/discovery/#") + + +@patch("homeassistant.components.mqtt.PLATFORMS", [Platform.SENSOR]) +async def test_clear_config_topic_disabled_entity( + hass, mqtt_mock_entry_no_yaml_config, device_reg, caplog +): + """Test the discovery topic is removed when a disabled entity is removed.""" + mqtt_mock = await mqtt_mock_entry_no_yaml_config() + # discover an entity that is not enabled by default + config = { + "name": "sbfspot_12345", + "state_topic": "homeassistant_test/sensor/sbfspot_0/sbfspot_12345/", + "unique_id": "sbfspot_12345", + "enabled_by_default": False, + "device": { + "identifiers": ["sbfspot_12345"], + "name": "sbfspot_12345", + "sw_version": "1.0", + "connections": [["mac", "12:34:56:AB:CD:EF"]], + }, + } + async_fire_mqtt_message( + hass, + "homeassistant/sensor/sbfspot_0/sbfspot_12345/config", + json.dumps(config), + ) + await hass.async_block_till_done() + # discover an entity that is not unique (part 1), will be added + config_not_unique1 = copy.deepcopy(config) + config_not_unique1["name"] = "sbfspot_12345_1" + config_not_unique1["unique_id"] = "not_unique" + config_not_unique1.pop("enabled_by_default") + async_fire_mqtt_message( + hass, + "homeassistant/sensor/sbfspot_0/sbfspot_12345_1/config", + json.dumps(config_not_unique1), + ) + # discover an entity that is not unique (part 2), will not be added + config_not_unique2 = copy.deepcopy(config_not_unique1) + config_not_unique2["name"] = "sbfspot_12345_2" + async_fire_mqtt_message( + hass, + "homeassistant/sensor/sbfspot_0/sbfspot_12345_2/config", + json.dumps(config_not_unique2), + ) + await hass.async_block_till_done() + assert "Platform mqtt does not generate unique IDs" in caplog.text + + assert hass.states.get("sensor.sbfspot_12345") is None # disabled + assert hass.states.get("sensor.sbfspot_12345_1") is not None # enabled + assert hass.states.get("sensor.sbfspot_12345_2") is None # not unique + + # Verify device is created + device_entry = device_reg.async_get_device(set(), {("mac", "12:34:56:AB:CD:EF")}) + assert device_entry is not None + + # Remove the device from the registry + device_reg.async_remove_device(device_entry.id) + await hass.async_block_till_done() + await hass.async_block_till_done() + + # Assert all valid discovery topics are cleared + assert mqtt_mock.async_publish.call_count == 2 + assert ( + call("homeassistant/sensor/sbfspot_0/sbfspot_12345/config", "", 0, True) + in mqtt_mock.async_publish.mock_calls + ) + assert ( + call("homeassistant/sensor/sbfspot_0/sbfspot_12345_1/config", "", 0, True) + in mqtt_mock.async_publish.mock_calls + ) + + +@patch("homeassistant.components.mqtt.PLATFORMS", [Platform.SENSOR]) +async def test_clean_up_registry_monitoring( + hass, mqtt_mock_entry_no_yaml_config, device_reg, tmp_path +): + """Test registry monitoring hook is removed after a reload.""" + await mqtt_mock_entry_no_yaml_config() + hooks: dict = hass.data[mqtt.const.DATA_MQTT_DISCOVERY_REGISTRY_HOOKS] + # discover an entity that is not enabled by default + config1 = { + "name": "sbfspot_12345", + "state_topic": "homeassistant_test/sensor/sbfspot_0/sbfspot_12345/", + "unique_id": "sbfspot_12345", + "enabled_by_default": False, + "device": { + "identifiers": ["sbfspot_12345"], + "name": "sbfspot_12345", + "sw_version": "1.0", + "connections": [["mac", "12:34:56:AB:CD:EF"]], + }, + } + # Publish it config + # Since it is not enabled_by_default the sensor will not be loaded + # it should register a hook for monitoring the entiry registry + async_fire_mqtt_message( + hass, + "homeassistant/sensor/sbfspot_0/sbfspot_12345/config", + json.dumps(config1), + ) + await hass.async_block_till_done() + assert len(hooks) == 1 + + # Publish it again no new monitor should be started + async_fire_mqtt_message( + hass, + "homeassistant/sensor/sbfspot_0/sbfspot_12345/config", + json.dumps(config1), + ) + await hass.async_block_till_done() + assert len(hooks) == 1 + + # Verify device is created + device_entry = device_reg.async_get_device(set(), {("mac", "12:34:56:AB:CD:EF")}) + assert device_entry is not None + + # Enload the entry + # The monitoring should be cleared + await help_test_unload_config_entry(hass, tmp_path, {}) + assert len(hooks) == 0 + + +@patch("homeassistant.components.mqtt.PLATFORMS", [Platform.SENSOR]) +async def test_unique_id_collission_has_priority( + hass, mqtt_mock_entry_no_yaml_config, entity_reg +): + """Test tehe unique_id collision detection has priority over registry disabled items.""" + await mqtt_mock_entry_no_yaml_config() + config = { + "name": "sbfspot_12345", + "state_topic": "homeassistant_test/sensor/sbfspot_0/sbfspot_12345/", + "unique_id": "sbfspot_12345", + "enabled_by_default": False, + "device": { + "identifiers": ["sbfspot_12345"], + "name": "sbfspot_12345", + "sw_version": "1.0", + "connections": [["mac", "12:34:56:AB:CD:EF"]], + }, + } + # discover an entity that is not unique and disabled by default (part 1), will be added + config_not_unique1 = copy.deepcopy(config) + config_not_unique1["name"] = "sbfspot_12345_1" + config_not_unique1["unique_id"] = "not_unique" + async_fire_mqtt_message( + hass, + "homeassistant/sensor/sbfspot_0/sbfspot_12345_1/config", + json.dumps(config_not_unique1), + ) + # discover an entity that is not unique (part 2), will not be added, and the registry entry is cleared + config_not_unique2 = copy.deepcopy(config_not_unique1) + config_not_unique2["name"] = "sbfspot_12345_2" + async_fire_mqtt_message( + hass, + "homeassistant/sensor/sbfspot_0/sbfspot_12345_2/config", + json.dumps(config_not_unique2), + ) + await hass.async_block_till_done() + + assert hass.states.get("sensor.sbfspot_12345_1") is None # not enabled + assert hass.states.get("sensor.sbfspot_12345_2") is None # not unique + + # Verify the first entity is created + assert entity_reg.async_get("sensor.sbfspot_12345_1") is not None + # Verify the second entity is not created because it is not unique + assert entity_reg.async_get("sensor.sbfspot_12345_2") is None From 4c0872b4e4d44ac651f8f3eb74a9a05abab7e6c9 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Fri, 9 Sep 2022 11:12:09 +0200 Subject: [PATCH 03/34] Improve warning messages on invalid received modes (#77909) --- .../components/mqtt/light/schema_json.py | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/mqtt/light/schema_json.py b/homeassistant/components/mqtt/light/schema_json.py index 295b43120d4..14eca9569f8 100644 --- a/homeassistant/components/mqtt/light/schema_json.py +++ b/homeassistant/components/mqtt/light/schema_json.py @@ -249,7 +249,9 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): except KeyError: pass except ValueError: - _LOGGER.warning("Invalid RGB color value received") + _LOGGER.warning( + "Invalid RGB color value received for entity %s", self.entity_id + ) return try: @@ -259,7 +261,9 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): except KeyError: pass except ValueError: - _LOGGER.warning("Invalid XY color value received") + _LOGGER.warning( + "Invalid XY color value received for entity %s", self.entity_id + ) return try: @@ -269,12 +273,16 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): except KeyError: pass except ValueError: - _LOGGER.warning("Invalid HS color value received") + _LOGGER.warning( + "Invalid HS color value received for entity %s", self.entity_id + ) return else: color_mode = values["color_mode"] if not self._supports_color_mode(color_mode): - _LOGGER.warning("Invalid color mode received") + _LOGGER.warning( + "Invalid color mode received for entity %s", self.entity_id + ) return try: if color_mode == ColorMode.COLOR_TEMP: @@ -314,7 +322,10 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): self._color_mode = ColorMode.XY self._xy = (x, y) except (KeyError, ValueError): - _LOGGER.warning("Invalid or incomplete color value received") + _LOGGER.warning( + "Invalid or incomplete color value received for entity %s", + self.entity_id, + ) def _prepare_subscribe_topics(self): """(Re)Subscribe to topics.""" @@ -351,7 +362,10 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): except KeyError: pass except (TypeError, ValueError): - _LOGGER.warning("Invalid brightness value received") + _LOGGER.warning( + "Invalid brightness value received for entity %s", + self.entity_id, + ) if ( self._supported_features @@ -366,7 +380,10 @@ class MqttLightJson(MqttEntity, LightEntity, RestoreEntity): except KeyError: pass except ValueError: - _LOGGER.warning("Invalid color temp value received") + _LOGGER.warning( + "Invalid color temp value received for entity %s", + self.entity_id, + ) if self._supported_features and LightEntityFeature.EFFECT: with suppress(KeyError): From f042cc5d7bed73a39d5869c606765a505a0cdc27 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 9 Sep 2022 01:47:33 -0400 Subject: [PATCH 04/34] Handle missing supported brands (#78090) --- homeassistant/components/websocket_api/commands.py | 3 +++ tests/components/websocket_api/test_commands.py | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/websocket_api/commands.py b/homeassistant/components/websocket_api/commands.py index 1761323a60d..d4596619241 100644 --- a/homeassistant/components/websocket_api/commands.py +++ b/homeassistant/components/websocket_api/commands.py @@ -722,6 +722,9 @@ async def handle_supported_brands( for int_or_exc in ints_or_excs.values(): if isinstance(int_or_exc, Exception): raise int_or_exc + # Happens if a custom component without supported brands overrides a built-in one with supported brands + if "supported_brands" not in int_or_exc.manifest: + continue data[int_or_exc.domain] = int_or_exc.manifest["supported_brands"] connection.send_result(msg["id"], data) diff --git a/tests/components/websocket_api/test_commands.py b/tests/components/websocket_api/test_commands.py index fe748e2c47c..354a4edeb0d 100644 --- a/tests/components/websocket_api/test_commands.py +++ b/tests/components/websocket_api/test_commands.py @@ -1760,6 +1760,12 @@ async def test_validate_config_invalid(websocket_client, key, config, error): async def test_supported_brands(hass, websocket_client): """Test supported brands.""" + # Custom components without supported brands that override a built-in component with + # supported brand will still be listed in HAS_SUPPORTED_BRANDS and should be ignored. + mock_integration( + hass, + MockModule("override_without_brands"), + ) mock_integration( hass, MockModule("test", partial_manifest={"supported_brands": {"hello": "World"}}), @@ -1773,7 +1779,7 @@ async def test_supported_brands(hass, websocket_client): with patch( "homeassistant.generated.supported_brands.HAS_SUPPORTED_BRANDS", - ("abcd", "test"), + ("abcd", "test", "override_without_brands"), ): await websocket_client.send_json({"id": 7, "type": "supported_brands"}) msg = await websocket_client.receive_json() From dc7c860c6ae5704f1b26ac091c947d76c60e7ddc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 9 Sep 2022 18:13:27 -0500 Subject: [PATCH 05/34] Fix switchbot writing state too frequently (#78094) --- .../components/switchbot/coordinator.py | 5 ++++- .../components/switchbot/manifest.json | 2 +- homeassistant/components/switchbot/sensor.py | 17 ++++++++++++++--- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/switchbot/coordinator.py b/homeassistant/components/switchbot/coordinator.py index 103e9d67c58..94018c1b46b 100644 --- a/homeassistant/components/switchbot/coordinator.py +++ b/homeassistant/components/switchbot/coordinator.py @@ -69,13 +69,16 @@ class SwitchbotDataUpdateCoordinator(PassiveBluetoothDataUpdateCoordinator): change: bluetooth.BluetoothChange, ) -> None: """Handle a Bluetooth event.""" + self.ble_device = service_info.device if adv := switchbot.parse_advertisement_data( service_info.device, service_info.advertisement ): - self.data = flatten_sensors_data(adv.data) if "modelName" in self.data: self._ready_event.set() _LOGGER.debug("%s: Switchbot data: %s", self.ble_device.address, self.data) + if not self.device.advertisement_changed(adv): + return + self.data = flatten_sensors_data(adv.data) self.device.update_from_advertisement(adv) super()._async_handle_bluetooth_event(service_info, change) diff --git a/homeassistant/components/switchbot/manifest.json b/homeassistant/components/switchbot/manifest.json index f322734ba54..e311295e52c 100644 --- a/homeassistant/components/switchbot/manifest.json +++ b/homeassistant/components/switchbot/manifest.json @@ -2,7 +2,7 @@ "domain": "switchbot", "name": "SwitchBot", "documentation": "https://www.home-assistant.io/integrations/switchbot", - "requirements": ["PySwitchbot==0.18.27"], + "requirements": ["PySwitchbot==0.19.0"], "config_flow": true, "dependencies": ["bluetooth"], "codeowners": [ diff --git a/homeassistant/components/switchbot/sensor.py b/homeassistant/components/switchbot/sensor.py index 886da1051b7..9658c1ed9c8 100644 --- a/homeassistant/components/switchbot/sensor.py +++ b/homeassistant/components/switchbot/sensor.py @@ -71,14 +71,16 @@ async def async_setup_entry( ) -> None: """Set up Switchbot sensor based on a config entry.""" coordinator: SwitchbotDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] - async_add_entities( + entities = [ SwitchBotSensor( coordinator, sensor, ) for sensor in coordinator.data["data"] if sensor in SENSOR_TYPES - ) + ] + entities.append(SwitchbotRSSISensor(coordinator, "rssi")) + async_add_entities(entities) class SwitchBotSensor(SwitchbotEntity, SensorEntity): @@ -98,6 +100,15 @@ class SwitchBotSensor(SwitchbotEntity, SensorEntity): self.entity_description = SENSOR_TYPES[sensor] @property - def native_value(self) -> str: + def native_value(self) -> str | int: """Return the state of the sensor.""" return self.data["data"][self._sensor] + + +class SwitchbotRSSISensor(SwitchBotSensor): + """Representation of a Switchbot RSSI sensor.""" + + @property + def native_value(self) -> str | int: + """Return the state of the sensor.""" + return self.coordinator.ble_device.rssi diff --git a/requirements_all.txt b/requirements_all.txt index e740b8d0017..49fcc1876ec 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -37,7 +37,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.18.27 +PySwitchbot==0.19.0 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 493ff7c4e5b..8ebf003e88e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -33,7 +33,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.18.27 +PySwitchbot==0.19.0 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 From 3ee62d619ff1ab8765cde735a73b650b0a312cdb Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Fri, 9 Sep 2022 14:43:54 +0200 Subject: [PATCH 06/34] Fix LIFX light turning on while fading off (#78095) --- homeassistant/components/lifx/light.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/lifx/light.py b/homeassistant/components/lifx/light.py index 36d3b480f74..4237fca9be7 100644 --- a/homeassistant/components/lifx/light.py +++ b/homeassistant/components/lifx/light.py @@ -219,15 +219,10 @@ class LIFXLight(LIFXEntity, LightEntity): elif power_on: await self.set_power(True, duration=fade) else: + if power_on: + await self.set_power(True) if hsbk: await self.set_color(hsbk, kwargs, duration=fade) - # The response from set_color will tell us if the - # bulb is actually on or not, so we don't need to - # call power_on if its already on - if power_on and self.bulb.power_level == 0: - await self.set_power(True) - elif power_on: - await self.set_power(True) if power_off: await self.set_power(False, duration=fade) From 125afb39f0e1ee62b563fba228c941c8e0af6ac0 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Fri, 9 Sep 2022 16:10:56 -0400 Subject: [PATCH 07/34] Fix zwave_js update entity (#78116) * Test zwave_js update entity progress * Block until firmware update is done * Update homeassistant/components/zwave_js/update.py Co-authored-by: Martin Hjelmare * revert params * unsub finished event listener * fix tests * Add test for returned failure * refactor a little * rename * Remove unnecessary controller logic for mocking * Clear event when resetting * Comments * readability * Fix test * Fix test Co-authored-by: Martin Hjelmare --- homeassistant/components/zwave_js/update.py | 71 +++++-- tests/components/zwave_js/test_update.py | 204 +++++++++++++++++--- 2 files changed, 232 insertions(+), 43 deletions(-) diff --git a/homeassistant/components/zwave_js/update.py b/homeassistant/components/zwave_js/update.py index 4f25d138aea..932ed46a0fc 100644 --- a/homeassistant/components/zwave_js/update.py +++ b/homeassistant/components/zwave_js/update.py @@ -12,7 +12,12 @@ from zwave_js_server.client import Client as ZwaveClient from zwave_js_server.const import NodeStatus from zwave_js_server.exceptions import BaseZwaveJSServerError, FailedZWaveCommand from zwave_js_server.model.driver import Driver -from zwave_js_server.model.firmware import FirmwareUpdateInfo, FirmwareUpdateProgress +from zwave_js_server.model.firmware import ( + FirmwareUpdateFinished, + FirmwareUpdateInfo, + FirmwareUpdateProgress, + FirmwareUpdateStatus, +) from zwave_js_server.model.node import Node as ZwaveNode from homeassistant.components.update import UpdateDeviceClass, UpdateEntity @@ -82,7 +87,10 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity): self._status_unsub: Callable[[], None] | None = None self._poll_unsub: Callable[[], None] | None = None self._progress_unsub: Callable[[], None] | None = None + self._finished_unsub: Callable[[], None] | None = None self._num_files_installed: int = 0 + self._finished_event = asyncio.Event() + self._finished_status: FirmwareUpdateStatus | None = None # Entity class attributes self._attr_name = "Firmware" @@ -119,18 +127,38 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity): self.async_write_ha_state() @callback - def _reset_progress(self) -> None: - """Reset update install progress.""" + def _update_finished(self, event: dict[str, Any]) -> None: + """Update install progress on event.""" + finished: FirmwareUpdateFinished = event["firmware_update_finished"] + self._finished_status = finished.status + self._finished_event.set() + + @callback + def _unsub_firmware_events_and_reset_progress( + self, write_state: bool = False + ) -> None: + """Unsubscribe from firmware events and reset update install progress.""" if self._progress_unsub: self._progress_unsub() self._progress_unsub = None + + if self._finished_unsub: + self._finished_unsub() + self._finished_unsub = None + + self._finished_status = None + self._finished_event.clear() self._num_files_installed = 0 - self._attr_in_progress = False - self.async_write_ha_state() + self._attr_in_progress = 0 + if write_state: + self.async_write_ha_state() async def _async_update(self, _: HomeAssistant | datetime | None = None) -> None: """Update the entity.""" self._poll_unsub = None + + # If device is asleep/dead, wait for it to wake up/become alive before + # attempting an update for status, event_name in ( (NodeStatus.ASLEEP, "wake up"), (NodeStatus.DEAD, "alive"), @@ -187,19 +215,40 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity): """Install an update.""" firmware = self._latest_version_firmware assert firmware - self._attr_in_progress = 0 - self.async_write_ha_state() + self._unsub_firmware_events_and_reset_progress(True) + self._progress_unsub = self.node.on( "firmware update progress", self._update_progress ) + self._finished_unsub = self.node.once( + "firmware update finished", self._update_finished + ) + for file in firmware.files: try: await self.driver.controller.async_begin_ota_firmware_update( self.node, file ) except BaseZwaveJSServerError as err: - self._reset_progress() + self._unsub_firmware_events_and_reset_progress() raise HomeAssistantError(err) from err + + # We need to block until we receive the `firmware update finished` event + await self._finished_event.wait() + assert self._finished_status is not None + + # If status is not OK, we should throw an error to let the user know + if self._finished_status not in ( + FirmwareUpdateStatus.OK_NO_RESTART, + FirmwareUpdateStatus.OK_RESTART_PENDING, + FirmwareUpdateStatus.OK_WAITING_FOR_ACTIVATION, + ): + status = self._finished_status + self._unsub_firmware_events_and_reset_progress() + raise HomeAssistantError(status.name.replace("_", " ").title()) + + # If we get here, the firmware installation was successful and we need to + # update progress accordingly self._num_files_installed += 1 self._attr_in_progress = floor( 100 * self._num_files_installed / len(firmware.files) @@ -208,7 +257,7 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity): self._attr_installed_version = self._attr_latest_version = firmware.version self._latest_version_firmware = None - self._reset_progress() + self._unsub_firmware_events_and_reset_progress() async def async_poll_value(self, _: bool) -> None: """Poll a value.""" @@ -255,6 +304,4 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity): self._poll_unsub() self._poll_unsub = None - if self._progress_unsub: - self._progress_unsub() - self._progress_unsub = None + self._unsub_firmware_events_and_reset_progress() diff --git a/tests/components/zwave_js/test_update.py b/tests/components/zwave_js/test_update.py index 76fecfdee6d..0b567d93106 100644 --- a/tests/components/zwave_js/test_update.py +++ b/tests/components/zwave_js/test_update.py @@ -1,9 +1,11 @@ """Test the Z-Wave JS update entities.""" +import asyncio from datetime import timedelta import pytest from zwave_js_server.event import Event from zwave_js_server.exceptions import FailedZWaveCommand +from zwave_js_server.model.firmware import FirmwareUpdateStatus from homeassistant.components.update.const import ( ATTR_AUTO_UPDATE, @@ -51,7 +53,7 @@ FIRMWARE_UPDATES = { } -async def test_update_entity_success( +async def test_update_entity_states( hass, client, climate_radio_thermostat_ct100_plus_different_endpoints, @@ -60,7 +62,7 @@ async def test_update_entity_success( caplog, hass_ws_client, ): - """Test update entity.""" + """Test update entity states.""" ws_client = await hass_ws_client(hass) await hass.async_block_till_done() @@ -137,39 +139,14 @@ async def test_update_entity_success( client.async_send_command.reset_mock() - # Test successful install call without a version - await hass.services.async_call( - UPDATE_DOMAIN, - SERVICE_INSTALL, - { - ATTR_ENTITY_ID: UPDATE_ENTITY, - }, - blocking=True, - ) - args = client.async_send_command.call_args_list[0][0][0] - assert args["command"] == "controller.begin_ota_firmware_update" - assert ( - args["nodeId"] - == climate_radio_thermostat_ct100_plus_different_endpoints.node_id - ) - assert args["update"] == { - "target": 0, - "url": "https://example2.com", - "integrity": "sha2", - } - - client.async_send_command.reset_mock() - - -async def test_update_entity_install_failure( +async def test_update_entity_install_raises( hass, client, climate_radio_thermostat_ct100_plus_different_endpoints, - controller_node, integration, ): - """Test update entity failed install.""" + """Test update entity install raises exception.""" client.async_send_command.return_value = FIRMWARE_UPDATES async_fire_time_changed(hass, dt_util.utcnow() + timedelta(days=1)) @@ -287,11 +264,10 @@ async def test_update_entity_ha_not_running( assert args["nodeId"] == zen_31.node_id -async def test_update_entity_failure( +async def test_update_entity_update_failure( hass, client, climate_radio_thermostat_ct100_plus_different_endpoints, - controller_node, integration, ): """Test update entity update failed.""" @@ -311,3 +287,169 @@ async def test_update_entity_failure( args["nodeId"] == climate_radio_thermostat_ct100_plus_different_endpoints.node_id ) + + +async def test_update_entity_progress( + hass, + client, + climate_radio_thermostat_ct100_plus_different_endpoints, + integration, +): + """Test update entity progress.""" + node = climate_radio_thermostat_ct100_plus_different_endpoints + client.async_send_command.return_value = FIRMWARE_UPDATES + + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(days=1)) + await hass.async_block_till_done() + + state = hass.states.get(UPDATE_ENTITY) + assert state + assert state.state == STATE_ON + attrs = state.attributes + assert attrs[ATTR_INSTALLED_VERSION] == "10.7" + assert attrs[ATTR_LATEST_VERSION] == "11.2.4" + + client.async_send_command.reset_mock() + client.async_send_command.return_value = None + + # Test successful install call without a version + install_task = hass.async_create_task( + hass.services.async_call( + UPDATE_DOMAIN, + SERVICE_INSTALL, + { + ATTR_ENTITY_ID: UPDATE_ENTITY, + }, + blocking=True, + ) + ) + + # Sleep so that task starts + await asyncio.sleep(0.1) + + event = Event( + type="firmware update progress", + data={ + "source": "node", + "event": "firmware update progress", + "nodeId": node.node_id, + "sentFragments": 1, + "totalFragments": 20, + }, + ) + node.receive_event(event) + + # Validate that the progress is updated + state = hass.states.get(UPDATE_ENTITY) + assert state + attrs = state.attributes + assert attrs[ATTR_IN_PROGRESS] == 5 + + event = Event( + type="firmware update finished", + data={ + "source": "node", + "event": "firmware update finished", + "nodeId": node.node_id, + "status": FirmwareUpdateStatus.OK_NO_RESTART, + }, + ) + + node.receive_event(event) + await hass.async_block_till_done() + + # Validate that progress is reset and entity reflects new version + state = hass.states.get(UPDATE_ENTITY) + assert state + attrs = state.attributes + assert attrs[ATTR_IN_PROGRESS] is False + assert attrs[ATTR_INSTALLED_VERSION] == "11.2.4" + assert attrs[ATTR_LATEST_VERSION] == "11.2.4" + assert state.state == STATE_OFF + + await install_task + + +async def test_update_entity_install_failed( + hass, + client, + climate_radio_thermostat_ct100_plus_different_endpoints, + integration, + caplog, +): + """Test update entity install returns error status.""" + node = climate_radio_thermostat_ct100_plus_different_endpoints + client.async_send_command.return_value = FIRMWARE_UPDATES + + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(days=1)) + await hass.async_block_till_done() + + state = hass.states.get(UPDATE_ENTITY) + assert state + assert state.state == STATE_ON + attrs = state.attributes + assert attrs[ATTR_INSTALLED_VERSION] == "10.7" + assert attrs[ATTR_LATEST_VERSION] == "11.2.4" + + client.async_send_command.reset_mock() + client.async_send_command.return_value = None + + async def call_install(): + await hass.services.async_call( + UPDATE_DOMAIN, + SERVICE_INSTALL, + { + ATTR_ENTITY_ID: UPDATE_ENTITY, + }, + blocking=True, + ) + + # Test install call - we expect it to raise + install_task = hass.async_create_task(call_install()) + + # Sleep so that task starts + await asyncio.sleep(0.1) + + event = Event( + type="firmware update progress", + data={ + "source": "node", + "event": "firmware update progress", + "nodeId": node.node_id, + "sentFragments": 1, + "totalFragments": 20, + }, + ) + node.receive_event(event) + + # Validate that the progress is updated + state = hass.states.get(UPDATE_ENTITY) + assert state + attrs = state.attributes + assert attrs[ATTR_IN_PROGRESS] == 5 + + event = Event( + type="firmware update finished", + data={ + "source": "node", + "event": "firmware update finished", + "nodeId": node.node_id, + "status": FirmwareUpdateStatus.ERROR_TIMEOUT, + }, + ) + + node.receive_event(event) + await hass.async_block_till_done() + + # Validate that progress is reset and entity reflects old version + state = hass.states.get(UPDATE_ENTITY) + assert state + attrs = state.attributes + assert attrs[ATTR_IN_PROGRESS] is False + assert attrs[ATTR_INSTALLED_VERSION] == "10.7" + assert attrs[ATTR_LATEST_VERSION] == "11.2.4" + assert state.state == STATE_ON + + # validate that the install task failed + with pytest.raises(HomeAssistantError): + await install_task From 2b961fd327c7538cea585622467471acedf6f8ab Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 9 Sep 2022 14:35:23 +0200 Subject: [PATCH 08/34] Improve unique_id collision checks in entity_platform (#78132) --- homeassistant/helpers/entity_platform.py | 82 +++++++++++++++--------- tests/helpers/test_entity_platform.py | 37 +++++++++-- 2 files changed, 83 insertions(+), 36 deletions(-) diff --git a/homeassistant/helpers/entity_platform.py b/homeassistant/helpers/entity_platform.py index c5c61cc1b0d..81487bbb627 100644 --- a/homeassistant/helpers/entity_platform.py +++ b/homeassistant/helpers/entity_platform.py @@ -454,6 +454,22 @@ class EntityPlatform: self.scan_interval, ) + def _entity_id_already_exists(self, entity_id: str) -> tuple[bool, bool]: + """Check if an entity_id already exists. + + Returns a tuple [already_exists, restored] + """ + already_exists = entity_id in self.entities + restored = False + + if not already_exists and not self.hass.states.async_available(entity_id): + existing = self.hass.states.get(entity_id) + if existing is not None and ATTR_RESTORED in existing.attributes: + restored = True + else: + already_exists = True + return (already_exists, restored) + async def _async_add_entity( # noqa: C901 self, entity: Entity, @@ -480,12 +496,31 @@ class EntityPlatform: entity.add_to_platform_abort() return - requested_entity_id = None suggested_object_id: str | None = None generate_new_entity_id = False # Get entity_id from unique ID registration if entity.unique_id is not None: + registered_entity_id = entity_registry.async_get_entity_id( + self.domain, self.platform_name, entity.unique_id + ) + if registered_entity_id: + already_exists, _ = self._entity_id_already_exists(registered_entity_id) + + if already_exists: + # If there's a collision, the entry belongs to another entity + entity.registry_entry = None + msg = ( + f"Platform {self.platform_name} does not generate unique IDs. " + ) + if entity.entity_id: + msg += f"ID {entity.unique_id} is already used by {registered_entity_id} - ignoring {entity.entity_id}" + else: + msg += f"ID {entity.unique_id} already exists - ignoring {registered_entity_id}" + self.logger.error(msg) + entity.add_to_platform_abort() + return + if self.config_entry is not None: config_entry_id: str | None = self.config_entry.entry_id else: @@ -541,7 +576,6 @@ class EntityPlatform: pass if entity.entity_id is not None: - requested_entity_id = entity.entity_id suggested_object_id = split_entity_id(entity.entity_id)[1] else: if device and entity.has_entity_name: # type: ignore[unreachable] @@ -592,16 +626,6 @@ class EntityPlatform: entity.registry_entry = entry entity.entity_id = entry.entity_id - if entry.disabled: - self.logger.debug( - "Not adding entity %s because it's disabled", - entry.name - or entity.name - or f'"{self.platform_name} {entity.unique_id}"', - ) - entity.add_to_platform_abort() - return - # We won't generate an entity ID if the platform has already set one # We will however make sure that platform cannot pick a registered ID elif entity.entity_id is not None and entity_registry.async_is_registered( @@ -628,28 +652,22 @@ class EntityPlatform: entity.add_to_platform_abort() raise HomeAssistantError(f"Invalid entity ID: {entity.entity_id}") - already_exists = entity.entity_id in self.entities - restored = False - - if not already_exists and not self.hass.states.async_available( - entity.entity_id - ): - existing = self.hass.states.get(entity.entity_id) - if existing is not None and ATTR_RESTORED in existing.attributes: - restored = True - else: - already_exists = True + already_exists, restored = self._entity_id_already_exists(entity.entity_id) if already_exists: - if entity.unique_id is not None: - msg = f"Platform {self.platform_name} does not generate unique IDs. " - if requested_entity_id: - msg += f"ID {entity.unique_id} is already used by {entity.entity_id} - ignoring {requested_entity_id}" - else: - msg += f"ID {entity.unique_id} already exists - ignoring {entity.entity_id}" - else: - msg = f"Entity id already exists - ignoring: {entity.entity_id}" - self.logger.error(msg) + self.logger.error( + f"Entity id already exists - ignoring: {entity.entity_id}" + ) + entity.add_to_platform_abort() + return + + if entity.registry_entry and entity.registry_entry.disabled: + self.logger.debug( + "Not adding entity %s because it's disabled", + entry.name + or entity.name + or f'"{self.platform_name} {entity.unique_id}"', + ) entity.add_to_platform_abort() return diff --git a/tests/helpers/test_entity_platform.py b/tests/helpers/test_entity_platform.py index d7f77eeacda..b2af85ca631 100644 --- a/tests/helpers/test_entity_platform.py +++ b/tests/helpers/test_entity_platform.py @@ -438,13 +438,15 @@ async def test_async_remove_with_platform_update_finishes(hass): async def test_not_adding_duplicate_entities_with_unique_id(hass, caplog): - """Test for not adding duplicate entities.""" + """Test for not adding duplicate entities. + + Also test that the entity registry is not updated for duplicates. + """ caplog.set_level(logging.ERROR) component = EntityComponent(_LOGGER, DOMAIN, hass) - await component.async_add_entities( - [MockEntity(name="test1", unique_id="not_very_unique")] - ) + ent1 = MockEntity(name="test1", unique_id="not_very_unique") + await component.async_add_entities([ent1]) assert len(hass.states.async_entity_ids()) == 1 assert not caplog.text @@ -466,6 +468,11 @@ async def test_not_adding_duplicate_entities_with_unique_id(hass, caplog): assert ent2.platform is None assert len(hass.states.async_entity_ids()) == 1 + registry = er.async_get(hass) + # test the entity name was not updated + entry = registry.async_get_or_create(DOMAIN, DOMAIN, "not_very_unique") + assert entry.original_name == "test1" + async def test_using_prescribed_entity_id(hass): """Test for using predefined entity ID.""" @@ -577,6 +584,28 @@ async def test_registry_respect_entity_disabled(hass): assert hass.states.async_entity_ids() == [] +async def test_unique_id_conflict_has_priority_over_disabled_entity(hass, caplog): + """Test that an entity that is not unique has priority over a disabled entity.""" + component = EntityComponent(_LOGGER, DOMAIN, hass) + entity1 = MockEntity( + name="test1", unique_id="not_very_unique", enabled_by_default=False + ) + entity2 = MockEntity( + name="test2", unique_id="not_very_unique", enabled_by_default=False + ) + await component.async_add_entities([entity1]) + await component.async_add_entities([entity2]) + + assert len(hass.states.async_entity_ids()) == 1 + assert "Platform test_domain does not generate unique IDs." in caplog.text + assert entity1.registry_entry is not None + assert entity2.registry_entry is None + registry = er.async_get(hass) + # test the entity name was not updated + entry = registry.async_get_or_create(DOMAIN, DOMAIN, "not_very_unique") + assert entry.original_name == "test1" + + async def test_entity_registry_updates_name(hass): """Test that updates on the entity registry update platform entities.""" registry = mock_registry( From 27c0a37053c0392b3488f150f8652966e408aea5 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 9 Sep 2022 16:05:14 +0200 Subject: [PATCH 09/34] Allow non-integers in threshold sensor config flow (#78137) --- homeassistant/components/threshold/config_flow.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/threshold/config_flow.py b/homeassistant/components/threshold/config_flow.py index 1e6236259bd..45ccdcb4a5c 100644 --- a/homeassistant/components/threshold/config_flow.py +++ b/homeassistant/components/threshold/config_flow.py @@ -30,13 +30,19 @@ OPTIONS_SCHEMA = vol.Schema( vol.Required( CONF_HYSTERESIS, default=DEFAULT_HYSTERESIS ): selector.NumberSelector( - selector.NumberSelectorConfig(mode=selector.NumberSelectorMode.BOX), + selector.NumberSelectorConfig( + mode=selector.NumberSelectorMode.BOX, step=1e-3 + ), ), vol.Optional(CONF_LOWER): selector.NumberSelector( - selector.NumberSelectorConfig(mode=selector.NumberSelectorMode.BOX), + selector.NumberSelectorConfig( + mode=selector.NumberSelectorMode.BOX, step=1e-3 + ), ), vol.Optional(CONF_UPPER): selector.NumberSelector( - selector.NumberSelectorConfig(mode=selector.NumberSelectorMode.BOX), + selector.NumberSelectorConfig( + mode=selector.NumberSelectorMode.BOX, step=1e-3 + ), ), } ) From 06116f76fa18a072698c33544562fae84e0ad0c6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 9 Sep 2022 12:56:21 -0500 Subject: [PATCH 10/34] Bump bluetooth-adapters to 0.3.6 (#78138) --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index 3043d6412a4..cda4158086f 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -6,7 +6,7 @@ "quality_scale": "internal", "requirements": [ "bleak==0.16.0", - "bluetooth-adapters==0.3.5", + "bluetooth-adapters==0.3.6", "bluetooth-auto-recovery==0.3.2" ], "codeowners": ["@bdraco"], diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 1a1fcb9da84..bf93aeba6e5 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -11,7 +11,7 @@ attrs==21.2.0 awesomeversion==22.8.0 bcrypt==3.1.7 bleak==0.16.0 -bluetooth-adapters==0.3.5 +bluetooth-adapters==0.3.6 bluetooth-auto-recovery==0.3.2 certifi>=2021.5.30 ciso8601==2.2.0 diff --git a/requirements_all.txt b/requirements_all.txt index 49fcc1876ec..9328cb40a0c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -430,7 +430,7 @@ bluemaestro-ble==0.2.0 # bluepy==1.3.0 # homeassistant.components.bluetooth -bluetooth-adapters==0.3.5 +bluetooth-adapters==0.3.6 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.3.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8ebf003e88e..3de7f120672 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -341,7 +341,7 @@ blinkpy==0.19.0 bluemaestro-ble==0.2.0 # homeassistant.components.bluetooth -bluetooth-adapters==0.3.5 +bluetooth-adapters==0.3.6 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.3.2 From b24f3725d69dc7148aa6441f6f2b45c415d86b3f Mon Sep 17 00:00:00 2001 From: Yevhenii Vaskivskyi Date: Fri, 9 Sep 2022 16:36:48 +0200 Subject: [PATCH 11/34] Add missing strings for errors in amberelectric config flow (#78140) --- homeassistant/components/amberelectric/strings.json | 5 +++++ homeassistant/components/amberelectric/translations/en.json | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/homeassistant/components/amberelectric/strings.json b/homeassistant/components/amberelectric/strings.json index 61d2c061955..5235a8bf325 100644 --- a/homeassistant/components/amberelectric/strings.json +++ b/homeassistant/components/amberelectric/strings.json @@ -15,6 +15,11 @@ }, "description": "Select the NMI of the site you would like to add" } + }, + "error": { + "invalid_api_token": "[%key:common::config_flow::error::invalid_api_key%]", + "no_site": "No site provided", + "unknown_error": "[%key:common::config_flow::error::unknown%]" } } } diff --git a/homeassistant/components/amberelectric/translations/en.json b/homeassistant/components/amberelectric/translations/en.json index b4d30925aa7..0a974298134 100644 --- a/homeassistant/components/amberelectric/translations/en.json +++ b/homeassistant/components/amberelectric/translations/en.json @@ -15,6 +15,11 @@ }, "description": "Go to {api_url} to generate an API key" } + }, + "error": { + "invalid_api_token": "Invalid API key", + "no_site": "No site provided", + "unknown_error": "Unexpected error" } } } \ No newline at end of file From 78802c84804ff7a96f8a92ff2ec0626eb85a8ae0 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sat, 10 Sep 2022 04:31:10 +0200 Subject: [PATCH 12/34] Bump aioecowitt to 2022.09.1 (#78142) --- homeassistant/components/ecowitt/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/ecowitt/manifest.json b/homeassistant/components/ecowitt/manifest.json index 348df17b0cd..080b7d5af72 100644 --- a/homeassistant/components/ecowitt/manifest.json +++ b/homeassistant/components/ecowitt/manifest.json @@ -3,7 +3,7 @@ "name": "Ecowitt", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/ecowitt", - "requirements": ["aioecowitt==2022.08.3"], + "requirements": ["aioecowitt==2022.09.1"], "codeowners": ["@pvizeli"], "iot_class": "local_push" } diff --git a/requirements_all.txt b/requirements_all.txt index 9328cb40a0c..00adc14d3ca 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -147,7 +147,7 @@ aioeafm==0.1.2 aioeagle==1.1.0 # homeassistant.components.ecowitt -aioecowitt==2022.08.3 +aioecowitt==2022.09.1 # homeassistant.components.emonitor aioemonitor==1.0.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3de7f120672..cf3341f5728 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -134,7 +134,7 @@ aioeafm==0.1.2 aioeagle==1.1.0 # homeassistant.components.ecowitt -aioecowitt==2022.08.3 +aioecowitt==2022.09.1 # homeassistant.components.emonitor aioemonitor==1.0.5 From 258791626e897396a11819429cf746ae069da840 Mon Sep 17 00:00:00 2001 From: Jc2k Date: Sat, 10 Sep 2022 03:31:51 +0100 Subject: [PATCH 13/34] Add missing moisture sensor to xiaomi_ble (#78160) --- .../components/xiaomi_ble/binary_sensor.py | 3 ++ .../xiaomi_ble/test_binary_sensor.py | 45 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/homeassistant/components/xiaomi_ble/binary_sensor.py b/homeassistant/components/xiaomi_ble/binary_sensor.py index 448b1f176e5..4de491ab9dd 100644 --- a/homeassistant/components/xiaomi_ble/binary_sensor.py +++ b/homeassistant/components/xiaomi_ble/binary_sensor.py @@ -39,6 +39,9 @@ BINARY_SENSOR_DESCRIPTIONS = { key=XiaomiBinarySensorDeviceClass.SMOKE, device_class=BinarySensorDeviceClass.SMOKE, ), + XiaomiBinarySensorDeviceClass.MOISTURE: BinarySensorEntityDescription( + key=XiaomiBinarySensorDeviceClass.MOISTURE, + ), } diff --git a/tests/components/xiaomi_ble/test_binary_sensor.py b/tests/components/xiaomi_ble/test_binary_sensor.py index 03e3d52d783..390c8d4b579 100644 --- a/tests/components/xiaomi_ble/test_binary_sensor.py +++ b/tests/components/xiaomi_ble/test_binary_sensor.py @@ -52,3 +52,48 @@ async def test_smoke_sensor(hass): assert await hass.config_entries.async_unload(entry.entry_id) await hass.async_block_till_done() + + +async def test_moisture(hass): + """Make sure that formldehyde sensors are correctly mapped.""" + entry = MockConfigEntry( + domain=DOMAIN, + unique_id="C4:7C:8D:6A:3E:7A", + ) + entry.add_to_hass(hass) + + saved_callback = None + + def _async_register_callback(_hass, _callback, _matcher, _mode): + nonlocal saved_callback + saved_callback = _callback + return lambda: None + + with patch( + "homeassistant.components.bluetooth.update_coordinator.async_register_callback", + _async_register_callback, + ): + assert await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + assert len(hass.states.async_all()) == 0 + + # WARNING: This test data is synthetic, rather than captured from a real device + # obj type is 0x1014, payload len is 0x2 and payload is 0xf400 + saved_callback( + make_advertisement( + "C4:7C:8D:6A:3E:7A", b"q \x5d\x01iz>j\x8d|\xc4\r\x14\x10\x02\xf4\x00" + ), + BluetoothChange.ADVERTISEMENT, + ) + + await hass.async_block_till_done() + assert len(hass.states.async_all()) == 1 + + sensor = hass.states.get("binary_sensor.smart_flower_pot_6a3e7a_moisture") + sensor_attr = sensor.attributes + assert sensor.state == "on" + assert sensor_attr[ATTR_FRIENDLY_NAME] == "Smart Flower Pot 6A3E7A Moisture" + + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() From e4aab6a818840d465e82c408caca59fdbf9bbeb2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 9 Sep 2022 21:32:17 -0500 Subject: [PATCH 14/34] Bump pySwitchbot to 0.19.1 (#78168) --- homeassistant/components/switchbot/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/switchbot/manifest.json b/homeassistant/components/switchbot/manifest.json index e311295e52c..7c5b0d9bc8d 100644 --- a/homeassistant/components/switchbot/manifest.json +++ b/homeassistant/components/switchbot/manifest.json @@ -2,7 +2,7 @@ "domain": "switchbot", "name": "SwitchBot", "documentation": "https://www.home-assistant.io/integrations/switchbot", - "requirements": ["PySwitchbot==0.19.0"], + "requirements": ["PySwitchbot==0.19.1"], "config_flow": true, "dependencies": ["bluetooth"], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index 00adc14d3ca..ea22719551b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -37,7 +37,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.19.0 +PySwitchbot==0.19.1 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index cf3341f5728..ff7b7c798e4 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -33,7 +33,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.19.0 +PySwitchbot==0.19.1 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 From 57717f13fc5d470f89e94c50b6ace1662170029c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 9 Sep 2022 22:39:13 -0400 Subject: [PATCH 15/34] Bumped version to 2022.9.2 --- homeassistant/const.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 38a50aa8fc3..f1b41fd57e0 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 = 9 -PATCH_VERSION: Final = "1" +PATCH_VERSION: Final = "2" __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/pyproject.toml b/pyproject.toml index 10bab1ac2f7..d411a1c62bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2022.9.1" +version = "2022.9.2" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst" From 1789a8a3852041864b027b686f8134c386bf4d4a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 9 Sep 2022 22:20:39 -0500 Subject: [PATCH 16/34] Bump aiohomekit to 1.5.3 (#78170) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 08eac050c98..1055779d7cc 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==1.5.2"], + "requirements": ["aiohomekit==1.5.3"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index ea22719551b..8f171702ced 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -171,7 +171,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==1.5.2 +aiohomekit==1.5.3 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ff7b7c798e4..4614825f9fe 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -155,7 +155,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==1.5.2 +aiohomekit==1.5.3 # homeassistant.components.emulated_hue # homeassistant.components.http From c731e2f125544898faf2fdf2991e6f57dd945b36 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 9 Sep 2022 23:32:14 -0400 Subject: [PATCH 17/34] Fix ecowitt typing (#78171) --- homeassistant/components/ecowitt/binary_sensor.py | 2 +- homeassistant/components/ecowitt/diagnostics.py | 4 ++-- homeassistant/components/ecowitt/sensor.py | 5 ++++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/ecowitt/binary_sensor.py b/homeassistant/components/ecowitt/binary_sensor.py index e487009d74b..fbe2e017339 100644 --- a/homeassistant/components/ecowitt/binary_sensor.py +++ b/homeassistant/components/ecowitt/binary_sensor.py @@ -68,4 +68,4 @@ class EcowittBinarySensorEntity(EcowittEntity, BinarySensorEntity): @property def is_on(self) -> bool: """Return true if the binary sensor is on.""" - return self.ecowitt.value > 0 + return bool(self.ecowitt.value) diff --git a/homeassistant/components/ecowitt/diagnostics.py b/homeassistant/components/ecowitt/diagnostics.py index d02a5dadbcc..96fa020667b 100644 --- a/homeassistant/components/ecowitt/diagnostics.py +++ b/homeassistant/components/ecowitt/diagnostics.py @@ -25,13 +25,13 @@ async def async_get_device_diagnostics( "device": { "name": station.station, "model": station.model, - "frequency": station.frequency, + "frequency": station.frequence, "version": station.version, }, "raw": ecowitt.last_values[station_id], "sensors": { sensor.key: sensor.value - for sensor in station.sensors + for sensor in ecowitt.sensors.values() if sensor.station.key == station_id }, } diff --git a/homeassistant/components/ecowitt/sensor.py b/homeassistant/components/ecowitt/sensor.py index 843dc700dc0..bb580b6d4b7 100644 --- a/homeassistant/components/ecowitt/sensor.py +++ b/homeassistant/components/ecowitt/sensor.py @@ -1,5 +1,8 @@ """Support for Ecowitt Weather Stations.""" +from __future__ import annotations + import dataclasses +from datetime import datetime from typing import Final from aioecowitt import EcoWittListener, EcoWittSensor, EcoWittSensorTypes @@ -242,6 +245,6 @@ class EcowittSensorEntity(EcowittEntity, SensorEntity): self.entity_description = description @property - def native_value(self) -> StateType: + def native_value(self) -> StateType | datetime: """Return the state of the sensor.""" return self.ecowitt.value From cdd5c809bb702cd57fc128974d08ce59447eb40d Mon Sep 17 00:00:00 2001 From: Rami Mosleh Date: Sat, 10 Sep 2022 20:35:26 +0300 Subject: [PATCH 18/34] Fix sending notification to multiple targets in Pushover (#78111) fix sending to mulitple targets --- homeassistant/components/pushover/notify.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/pushover/notify.py b/homeassistant/components/pushover/notify.py index dd711c80aaf..bcf47264108 100644 --- a/homeassistant/components/pushover/notify.py +++ b/homeassistant/components/pushover/notify.py @@ -128,7 +128,7 @@ class PushoverNotificationService(BaseNotificationService): self.pushover.send_message( self._user_key, message, - kwargs.get(ATTR_TARGET), + ",".join(kwargs.get(ATTR_TARGET, [])), title, url, url_title, From 795be361b460a945d4c4eea2fa09d244964bfe6d Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Sat, 10 Sep 2022 17:02:36 +0200 Subject: [PATCH 19/34] Add dependencies to ecowitt (#78187) --- homeassistant/components/ecowitt/manifest.json | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/ecowitt/manifest.json b/homeassistant/components/ecowitt/manifest.json index 080b7d5af72..224b4440e36 100644 --- a/homeassistant/components/ecowitt/manifest.json +++ b/homeassistant/components/ecowitt/manifest.json @@ -3,6 +3,7 @@ "name": "Ecowitt", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/ecowitt", + "dependencies": ["webhook"], "requirements": ["aioecowitt==2022.09.1"], "codeowners": ["@pvizeli"], "iot_class": "local_push" From 5f907601768df20e431ca1353bd14faaa6019f08 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 10 Sep 2022 12:30:30 -0500 Subject: [PATCH 20/34] Bump led-ble to 0.8.3 (#78188) * Bump led-ble to 0.8.0 Fixes setup when the previous shutdown was not clean and the device is still connected * bump again * bump again * bump again --- homeassistant/components/led_ble/__init__.py | 6 ++++-- homeassistant/components/led_ble/light.py | 4 ++-- homeassistant/components/led_ble/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/led_ble/__init__.py b/homeassistant/components/led_ble/__init__.py index d885b3eb950..5c454c6df7c 100644 --- a/homeassistant/components/led_ble/__init__.py +++ b/homeassistant/components/led_ble/__init__.py @@ -6,7 +6,7 @@ from datetime import timedelta import logging import async_timeout -from led_ble import BLEAK_EXCEPTIONS, LEDBLE +from led_ble import BLEAK_EXCEPTIONS, LEDBLE, get_device from homeassistant.components import bluetooth from homeassistant.components.bluetooth.match import ADDRESS, BluetoothCallbackMatcher @@ -27,7 +27,9 @@ _LOGGER = logging.getLogger(__name__) async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up LED BLE from a config entry.""" address: str = entry.data[CONF_ADDRESS] - ble_device = bluetooth.async_ble_device_from_address(hass, address.upper(), True) + ble_device = bluetooth.async_ble_device_from_address( + hass, address.upper(), True + ) or await get_device(address) if not ble_device: raise ConfigEntryNotReady( f"Could not find LED BLE device with address {address}" diff --git a/homeassistant/components/led_ble/light.py b/homeassistant/components/led_ble/light.py index a18ab812b19..4a8ff3f01af 100644 --- a/homeassistant/components/led_ble/light.py +++ b/homeassistant/components/led_ble/light.py @@ -48,12 +48,12 @@ class LEDBLEEntity(CoordinatorEntity, LightEntity): """Initialize an ledble light.""" super().__init__(coordinator) self._device = device - self._attr_unique_id = device._address + self._attr_unique_id = device.address self._attr_device_info = DeviceInfo( name=name, model=hex(device.model_num), sw_version=hex(device.version_num), - connections={(dr.CONNECTION_BLUETOOTH, device._address)}, + connections={(dr.CONNECTION_BLUETOOTH, device.address)}, ) self._async_update_attrs() diff --git a/homeassistant/components/led_ble/manifest.json b/homeassistant/components/led_ble/manifest.json index 1dd289daa4d..261d27726e5 100644 --- a/homeassistant/components/led_ble/manifest.json +++ b/homeassistant/components/led_ble/manifest.json @@ -3,7 +3,7 @@ "name": "LED BLE", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/ble_ble", - "requirements": ["led-ble==0.7.1"], + "requirements": ["led-ble==0.8.3"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "bluetooth": [ diff --git a/requirements_all.txt b/requirements_all.txt index 8f171702ced..2bc648329ab 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -968,7 +968,7 @@ lakeside==0.12 laundrify_aio==1.1.2 # homeassistant.components.led_ble -led-ble==0.7.1 +led-ble==0.8.3 # homeassistant.components.foscam libpyfoscam==1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4614825f9fe..bc8692ee18c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -706,7 +706,7 @@ lacrosse-view==0.0.9 laundrify_aio==1.1.2 # homeassistant.components.led_ble -led-ble==0.7.1 +led-ble==0.8.3 # homeassistant.components.foscam libpyfoscam==1.0 From a969ce273aa778c9ee3e4cb4935132b1ca186548 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 10 Sep 2022 12:32:38 -0500 Subject: [PATCH 21/34] Fix switchbot not setting up when already connected at startup (#78198) --- homeassistant/components/switchbot/__init__.py | 2 +- homeassistant/components/switchbot/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/switchbot/__init__.py b/homeassistant/components/switchbot/__init__.py index 345190d8933..d1f68047a83 100644 --- a/homeassistant/components/switchbot/__init__.py +++ b/homeassistant/components/switchbot/__init__.py @@ -84,7 +84,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: address: str = entry.data[CONF_ADDRESS] ble_device = bluetooth.async_ble_device_from_address( hass, address.upper(), connectable - ) + ) or await switchbot.get_device(address) if not ble_device: raise ConfigEntryNotReady( f"Could not find Switchbot {sensor_type} with address {address}" diff --git a/homeassistant/components/switchbot/manifest.json b/homeassistant/components/switchbot/manifest.json index 7c5b0d9bc8d..f35f3cddf72 100644 --- a/homeassistant/components/switchbot/manifest.json +++ b/homeassistant/components/switchbot/manifest.json @@ -2,7 +2,7 @@ "domain": "switchbot", "name": "SwitchBot", "documentation": "https://www.home-assistant.io/integrations/switchbot", - "requirements": ["PySwitchbot==0.19.1"], + "requirements": ["PySwitchbot==0.19.2"], "config_flow": true, "dependencies": ["bluetooth"], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index 2bc648329ab..785232fdb20 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -37,7 +37,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.19.1 +PySwitchbot==0.19.2 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index bc8692ee18c..7b741c06781 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -33,7 +33,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.19.1 +PySwitchbot==0.19.2 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 From 95c20df367b63b021ae143802ab55782cbc7f335 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 10 Sep 2022 12:32:29 -0500 Subject: [PATCH 22/34] Fix Yale Access Bluetooth not setting up when already connected at startup (#78199) --- homeassistant/components/yalexs_ble/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/yalexs_ble/manifest.json b/homeassistant/components/yalexs_ble/manifest.json index de0034c755f..da4bf1cf6d2 100644 --- a/homeassistant/components/yalexs_ble/manifest.json +++ b/homeassistant/components/yalexs_ble/manifest.json @@ -3,7 +3,7 @@ "name": "Yale Access Bluetooth", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/yalexs_ble", - "requirements": ["yalexs-ble==1.6.4"], + "requirements": ["yalexs-ble==1.7.1"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "bluetooth": [{ "manufacturer_id": 465 }], diff --git a/requirements_all.txt b/requirements_all.txt index 785232fdb20..599a606ee74 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2542,7 +2542,7 @@ xs1-api-client==3.0.0 yalesmartalarmclient==0.3.9 # homeassistant.components.yalexs_ble -yalexs-ble==1.6.4 +yalexs-ble==1.7.1 # homeassistant.components.august yalexs==1.2.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7b741c06781..3ccb693bd23 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1746,7 +1746,7 @@ xmltodict==0.13.0 yalesmartalarmclient==0.3.9 # homeassistant.components.yalexs_ble -yalexs-ble==1.6.4 +yalexs-ble==1.7.1 # homeassistant.components.august yalexs==1.2.1 From 183c61b6cac4dd805ae5c5f41cd261008b5c3e88 Mon Sep 17 00:00:00 2001 From: puddly <32534428+puddly@users.noreply.github.com> Date: Sat, 10 Sep 2022 13:56:01 -0400 Subject: [PATCH 23/34] Bump ZHA dependencies (#78201) --- homeassistant/components/zha/manifest.json | 4 ++-- requirements_all.txt | 4 ++-- requirements_test_all.txt | 4 ++-- tests/components/zha/test_config_flow.py | 4 +++- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index 2e35427a70c..419e1c1452b 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -8,8 +8,8 @@ "pyserial==3.5", "pyserial-asyncio==0.6", "zha-quirks==0.0.79", - "zigpy-deconz==0.18.0", - "zigpy==0.50.2", + "zigpy-deconz==0.18.1", + "zigpy==0.50.3", "zigpy-xbee==0.15.0", "zigpy-zigate==0.9.2", "zigpy-znp==0.8.2" diff --git a/requirements_all.txt b/requirements_all.txt index 599a606ee74..dd909ca8205 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2578,7 +2578,7 @@ zhong_hong_hvac==1.0.9 ziggo-mediabox-xl==1.1.0 # homeassistant.components.zha -zigpy-deconz==0.18.0 +zigpy-deconz==0.18.1 # homeassistant.components.zha zigpy-xbee==0.15.0 @@ -2590,7 +2590,7 @@ zigpy-zigate==0.9.2 zigpy-znp==0.8.2 # homeassistant.components.zha -zigpy==0.50.2 +zigpy==0.50.3 # homeassistant.components.zoneminder zm-py==0.5.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3ccb693bd23..4450a6a26e6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1767,7 +1767,7 @@ zeroconf==0.39.1 zha-quirks==0.0.79 # homeassistant.components.zha -zigpy-deconz==0.18.0 +zigpy-deconz==0.18.1 # homeassistant.components.zha zigpy-xbee==0.15.0 @@ -1779,7 +1779,7 @@ zigpy-zigate==0.9.2 zigpy-znp==0.8.2 # homeassistant.components.zha -zigpy==0.50.2 +zigpy==0.50.3 # homeassistant.components.zwave_js zwave-js-server-python==0.41.1 diff --git a/tests/components/zha/test_config_flow.py b/tests/components/zha/test_config_flow.py index d65732a6ab8..5fc4b232634 100644 --- a/tests/components/zha/test_config_flow.py +++ b/tests/components/zha/test_config_flow.py @@ -2,11 +2,12 @@ import copy import json -from unittest.mock import AsyncMock, MagicMock, PropertyMock, patch +from unittest.mock import AsyncMock, MagicMock, PropertyMock, create_autospec, patch import uuid import pytest import serial.tools.list_ports +from zigpy.backups import BackupManager import zigpy.config from zigpy.config import CONF_DEVICE, CONF_DEVICE_PATH from zigpy.exceptions import NetworkNotFormed @@ -49,6 +50,7 @@ def disable_platform_only(): def mock_app(): """Mock zigpy app interface.""" mock_app = AsyncMock() + mock_app.backups = create_autospec(BackupManager, instance=True) mock_app.backups.backups = [] with patch( From c7cb0d1a07f5a4d4ae6ca7f1550e7f5866fd1d80 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 10 Sep 2022 13:21:10 -0500 Subject: [PATCH 24/34] Close stale switchbot connections at setup time (#78202) --- homeassistant/components/switchbot/__init__.py | 2 ++ homeassistant/components/switchbot/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/switchbot/__init__.py b/homeassistant/components/switchbot/__init__.py index d1f68047a83..7307187bf54 100644 --- a/homeassistant/components/switchbot/__init__.py +++ b/homeassistant/components/switchbot/__init__.py @@ -89,6 +89,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: raise ConfigEntryNotReady( f"Could not find Switchbot {sensor_type} with address {address}" ) + + await switchbot.close_stale_connections(ble_device) cls = CLASS_BY_DEVICE.get(sensor_type, switchbot.SwitchbotDevice) device = cls( device=ble_device, diff --git a/homeassistant/components/switchbot/manifest.json b/homeassistant/components/switchbot/manifest.json index f35f3cddf72..c83e575bd49 100644 --- a/homeassistant/components/switchbot/manifest.json +++ b/homeassistant/components/switchbot/manifest.json @@ -2,7 +2,7 @@ "domain": "switchbot", "name": "SwitchBot", "documentation": "https://www.home-assistant.io/integrations/switchbot", - "requirements": ["PySwitchbot==0.19.2"], + "requirements": ["PySwitchbot==0.19.3"], "config_flow": true, "dependencies": ["bluetooth"], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index dd909ca8205..3bd9e23e5f9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -37,7 +37,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.19.2 +PySwitchbot==0.19.3 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4450a6a26e6..3b858b0344a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -33,7 +33,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.19.2 +PySwitchbot==0.19.3 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 From a626ab4f1a40e27a3681d4bf1da69d16698cefa0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 10 Sep 2022 13:36:44 -0500 Subject: [PATCH 25/34] Bump aiohomekit to 1.5.4 to handle stale ble connections at startup (#78203) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 1055779d7cc..b71156c9bc7 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==1.5.3"], + "requirements": ["aiohomekit==1.5.4"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index 3bd9e23e5f9..0f10e099606 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -171,7 +171,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==1.5.3 +aiohomekit==1.5.4 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3b858b0344a..361b7212cf5 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -155,7 +155,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==1.5.3 +aiohomekit==1.5.4 # homeassistant.components.emulated_hue # homeassistant.components.http From 6e88b8d3d551ddf598e20afe6736c1f3c74e331b Mon Sep 17 00:00:00 2001 From: Vincent Knoop Pathuis <48653141+vpathuis@users.noreply.github.com> Date: Sun, 11 Sep 2022 18:18:01 +0200 Subject: [PATCH 26/34] Landis+Gyr integration: increase timeout and add debug logging (#78025) --- homeassistant/components/landisgyr_heat_meter/__init__.py | 4 +--- .../components/landisgyr_heat_meter/config_flow.py | 6 ++++-- homeassistant/components/landisgyr_heat_meter/const.py | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/landisgyr_heat_meter/__init__.py b/homeassistant/components/landisgyr_heat_meter/__init__.py index 4321f53bed6..3ef235ff8af 100644 --- a/homeassistant/components/landisgyr_heat_meter/__init__.py +++ b/homeassistant/components/landisgyr_heat_meter/__init__.py @@ -31,9 +31,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: _LOGGER.info("Polling on %s", entry.data[CONF_DEVICE]) return await hass.async_add_executor_job(api.read) - # No automatic polling and no initial refresh of data is being done at this point, - # to prevent battery drain. The user will have to do it manually. - + # Polling is only daily to prevent battery drain. coordinator = DataUpdateCoordinator( hass, _LOGGER, diff --git a/homeassistant/components/landisgyr_heat_meter/config_flow.py b/homeassistant/components/landisgyr_heat_meter/config_flow.py index e3dbbb7433b..2e244a9a65f 100644 --- a/homeassistant/components/landisgyr_heat_meter/config_flow.py +++ b/homeassistant/components/landisgyr_heat_meter/config_flow.py @@ -14,7 +14,7 @@ from homeassistant import config_entries from homeassistant.const import CONF_DEVICE from homeassistant.exceptions import HomeAssistantError -from .const import DOMAIN +from .const import DOMAIN, ULTRAHEAT_TIMEOUT _LOGGER = logging.getLogger(__name__) @@ -43,6 +43,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): dev_path = await self.hass.async_add_executor_job( get_serial_by_id, user_input[CONF_DEVICE] ) + _LOGGER.debug("Using this path : %s", dev_path) try: return await self.validate_and_create_entry(dev_path) @@ -76,6 +77,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Try to connect to the device path and return an entry.""" model, device_number = await self.validate_ultraheat(dev_path) + _LOGGER.debug("Got model %s and device_number %s", model, device_number) await self.async_set_unique_id(device_number) self._abort_if_unique_id_configured() data = { @@ -94,7 +96,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): reader = UltraheatReader(port) heat_meter = HeatMeterService(reader) try: - async with async_timeout.timeout(10): + async with async_timeout.timeout(ULTRAHEAT_TIMEOUT): # validate and retrieve the model and device number for a unique id data = await self.hass.async_add_executor_job(heat_meter.read) _LOGGER.debug("Got data from Ultraheat API: %s", data) diff --git a/homeassistant/components/landisgyr_heat_meter/const.py b/homeassistant/components/landisgyr_heat_meter/const.py index 70008890d1f..55a6c65892c 100644 --- a/homeassistant/components/landisgyr_heat_meter/const.py +++ b/homeassistant/components/landisgyr_heat_meter/const.py @@ -11,6 +11,7 @@ from homeassistant.helpers.entity import EntityCategory DOMAIN = "landisgyr_heat_meter" GJ_TO_MWH = 0.277778 # conversion factor +ULTRAHEAT_TIMEOUT = 30 # reading the IR port can take some time HEAT_METER_SENSOR_TYPES = ( SensorEntityDescription( From 1e8f461270aefbf2cd8e3f9398a6cc25b05c8f3d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 11 Sep 2022 03:06:13 -0500 Subject: [PATCH 27/34] Bump bluetooth-adapters to 0.4.1 (#78205) Switches to dbus-fast which fixes a file descriptor leak --- homeassistant/components/bluetooth/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/bluetooth/manifest.json b/homeassistant/components/bluetooth/manifest.json index cda4158086f..3c9ef7a85b7 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -6,7 +6,7 @@ "quality_scale": "internal", "requirements": [ "bleak==0.16.0", - "bluetooth-adapters==0.3.6", + "bluetooth-adapters==0.4.1", "bluetooth-auto-recovery==0.3.2" ], "codeowners": ["@bdraco"], diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index bf93aeba6e5..4f41518361f 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -11,7 +11,7 @@ attrs==21.2.0 awesomeversion==22.8.0 bcrypt==3.1.7 bleak==0.16.0 -bluetooth-adapters==0.3.6 +bluetooth-adapters==0.4.1 bluetooth-auto-recovery==0.3.2 certifi>=2021.5.30 ciso8601==2.2.0 diff --git a/requirements_all.txt b/requirements_all.txt index 0f10e099606..5122e4aeb5d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -430,7 +430,7 @@ bluemaestro-ble==0.2.0 # bluepy==1.3.0 # homeassistant.components.bluetooth -bluetooth-adapters==0.3.6 +bluetooth-adapters==0.4.1 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.3.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 361b7212cf5..b94fb9eb73f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -341,7 +341,7 @@ blinkpy==0.19.0 bluemaestro-ble==0.2.0 # homeassistant.components.bluetooth -bluetooth-adapters==0.3.6 +bluetooth-adapters==0.4.1 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.3.2 From 4b79e82e317e7c6cf1ba0f328e43ef01d6b3e07d Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Sun, 11 Sep 2022 02:17:36 -0600 Subject: [PATCH 28/34] Bump `regenmaschine` to 2022.09.1 (#78210) --- homeassistant/components/rainmachine/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/rainmachine/manifest.json b/homeassistant/components/rainmachine/manifest.json index a6cc86e5055..6a87110f6f6 100644 --- a/homeassistant/components/rainmachine/manifest.json +++ b/homeassistant/components/rainmachine/manifest.json @@ -3,7 +3,7 @@ "name": "RainMachine", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/rainmachine", - "requirements": ["regenmaschine==2022.09.0"], + "requirements": ["regenmaschine==2022.09.1"], "codeowners": ["@bachya"], "iot_class": "local_polling", "homekit": { diff --git a/requirements_all.txt b/requirements_all.txt index 5122e4aeb5d..fd9db0d274a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2118,7 +2118,7 @@ raincloudy==0.0.7 raspyrfm-client==1.2.8 # homeassistant.components.rainmachine -regenmaschine==2022.09.0 +regenmaschine==2022.09.1 # homeassistant.components.renault renault-api==0.1.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b94fb9eb73f..3c96e15c671 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1451,7 +1451,7 @@ radios==0.1.1 radiotherm==2.1.0 # homeassistant.components.rainmachine -regenmaschine==2022.09.0 +regenmaschine==2022.09.1 # homeassistant.components.renault renault-api==0.1.11 From 4d4a87ba05c5f2247a58dc6c027face195c1ecbc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 11 Sep 2022 03:04:05 -0500 Subject: [PATCH 29/34] Bump led_ble to 0.8.5 (#78215) * Bump led_ble to 0.8.4 Changelog: https://github.com/Bluetooth-Devices/led-ble/compare/v0.8.3...v0.8.4 * bump again --- homeassistant/components/led_ble/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/led_ble/manifest.json b/homeassistant/components/led_ble/manifest.json index 261d27726e5..9bcb3f860e1 100644 --- a/homeassistant/components/led_ble/manifest.json +++ b/homeassistant/components/led_ble/manifest.json @@ -3,7 +3,7 @@ "name": "LED BLE", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/ble_ble", - "requirements": ["led-ble==0.8.3"], + "requirements": ["led-ble==0.8.5"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "bluetooth": [ diff --git a/requirements_all.txt b/requirements_all.txt index fd9db0d274a..d8594b03594 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -968,7 +968,7 @@ lakeside==0.12 laundrify_aio==1.1.2 # homeassistant.components.led_ble -led-ble==0.8.3 +led-ble==0.8.5 # homeassistant.components.foscam libpyfoscam==1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3c96e15c671..f453226f93c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -706,7 +706,7 @@ lacrosse-view==0.0.9 laundrify_aio==1.1.2 # homeassistant.components.led_ble -led-ble==0.8.3 +led-ble==0.8.5 # homeassistant.components.foscam libpyfoscam==1.0 From de8b066a1d714ea3e43ca035c7a94abc064c4c12 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Sun, 11 Sep 2022 18:18:38 +0200 Subject: [PATCH 30/34] Bump pysensibo to 1.0.20 (#78222) --- homeassistant/components/sensibo/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sensibo/manifest.json b/homeassistant/components/sensibo/manifest.json index a2a7cbe3bd0..4f89148a21c 100644 --- a/homeassistant/components/sensibo/manifest.json +++ b/homeassistant/components/sensibo/manifest.json @@ -2,7 +2,7 @@ "domain": "sensibo", "name": "Sensibo", "documentation": "https://www.home-assistant.io/integrations/sensibo", - "requirements": ["pysensibo==1.0.19"], + "requirements": ["pysensibo==1.0.20"], "config_flow": true, "codeowners": ["@andrey-git", "@gjohansson-ST"], "iot_class": "cloud_polling", diff --git a/requirements_all.txt b/requirements_all.txt index d8594b03594..0c4ffbb8c78 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1838,7 +1838,7 @@ pysaj==0.0.16 pysdcp==1 # homeassistant.components.sensibo -pysensibo==1.0.19 +pysensibo==1.0.20 # homeassistant.components.serial # homeassistant.components.zha diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f453226f93c..5e125ce07a3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1285,7 +1285,7 @@ pyruckus==0.16 pysabnzbd==1.1.1 # homeassistant.components.sensibo -pysensibo==1.0.19 +pysensibo==1.0.20 # homeassistant.components.serial # homeassistant.components.zha From e7986a54a5403d692dc40256b49423aadd879083 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 11 Sep 2022 11:19:31 -0500 Subject: [PATCH 31/34] Bump PySwitchbot to 0.19.5 (#78224) --- homeassistant/components/switchbot/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/switchbot/manifest.json b/homeassistant/components/switchbot/manifest.json index c83e575bd49..e1e832f854a 100644 --- a/homeassistant/components/switchbot/manifest.json +++ b/homeassistant/components/switchbot/manifest.json @@ -2,7 +2,7 @@ "domain": "switchbot", "name": "SwitchBot", "documentation": "https://www.home-assistant.io/integrations/switchbot", - "requirements": ["PySwitchbot==0.19.3"], + "requirements": ["PySwitchbot==0.19.5"], "config_flow": true, "dependencies": ["bluetooth"], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index 0c4ffbb8c78..333809983d4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -37,7 +37,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.19.3 +PySwitchbot==0.19.5 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5e125ce07a3..9bb916a069e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -33,7 +33,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.19.3 +PySwitchbot==0.19.5 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 From 1b7a06912a6ed34389fda29ee552e18ed819cde6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 11 Sep 2022 11:18:52 -0500 Subject: [PATCH 32/34] Bump yalexs-ble to 1.8.1 (#78225) --- homeassistant/components/yalexs_ble/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/yalexs_ble/manifest.json b/homeassistant/components/yalexs_ble/manifest.json index da4bf1cf6d2..a685d750077 100644 --- a/homeassistant/components/yalexs_ble/manifest.json +++ b/homeassistant/components/yalexs_ble/manifest.json @@ -3,7 +3,7 @@ "name": "Yale Access Bluetooth", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/yalexs_ble", - "requirements": ["yalexs-ble==1.7.1"], + "requirements": ["yalexs-ble==1.8.1"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "bluetooth": [{ "manufacturer_id": 465 }], diff --git a/requirements_all.txt b/requirements_all.txt index 333809983d4..eede3512e9c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2542,7 +2542,7 @@ xs1-api-client==3.0.0 yalesmartalarmclient==0.3.9 # homeassistant.components.yalexs_ble -yalexs-ble==1.7.1 +yalexs-ble==1.8.1 # homeassistant.components.august yalexs==1.2.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9bb916a069e..892f5fbb9f2 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1746,7 +1746,7 @@ xmltodict==0.13.0 yalesmartalarmclient==0.3.9 # homeassistant.components.yalexs_ble -yalexs-ble==1.7.1 +yalexs-ble==1.8.1 # homeassistant.components.august yalexs==1.2.1 From a277664187abd7c5dd9519a8cd13c8b2b6c3ef23 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 11 Sep 2022 11:19:01 -0500 Subject: [PATCH 33/34] Bump led-ble to 0.9.1 (#78226) --- homeassistant/components/led_ble/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/led_ble/manifest.json b/homeassistant/components/led_ble/manifest.json index 9bcb3f860e1..1f27837b89e 100644 --- a/homeassistant/components/led_ble/manifest.json +++ b/homeassistant/components/led_ble/manifest.json @@ -3,7 +3,7 @@ "name": "LED BLE", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/ble_ble", - "requirements": ["led-ble==0.8.5"], + "requirements": ["led-ble==0.9.1"], "dependencies": ["bluetooth"], "codeowners": ["@bdraco"], "bluetooth": [ diff --git a/requirements_all.txt b/requirements_all.txt index eede3512e9c..aadcbd9cdd9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -968,7 +968,7 @@ lakeside==0.12 laundrify_aio==1.1.2 # homeassistant.components.led_ble -led-ble==0.8.5 +led-ble==0.9.1 # homeassistant.components.foscam libpyfoscam==1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 892f5fbb9f2..0edfe2c7e8d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -706,7 +706,7 @@ lacrosse-view==0.0.9 laundrify_aio==1.1.2 # homeassistant.components.led_ble -led-ble==0.8.5 +led-ble==0.9.1 # homeassistant.components.foscam libpyfoscam==1.0 From 296db8b2afa2794c67b0c5a5ecc6af19cf15c225 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 11 Sep 2022 11:19:12 -0500 Subject: [PATCH 34/34] Bump aiohomekit to 1.5.6 (#78228) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index b71156c9bc7..a4cbbc227d9 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -3,7 +3,7 @@ "name": "HomeKit Controller", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", - "requirements": ["aiohomekit==1.5.4"], + "requirements": ["aiohomekit==1.5.6"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."], "bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }], "dependencies": ["bluetooth", "zeroconf"], diff --git a/requirements_all.txt b/requirements_all.txt index aadcbd9cdd9..4038e1863f4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -171,7 +171,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==1.5.4 +aiohomekit==1.5.6 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0edfe2c7e8d..3f072512375 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -155,7 +155,7 @@ aioguardian==2022.07.0 aioharmony==0.2.9 # homeassistant.components.homekit_controller -aiohomekit==1.5.4 +aiohomekit==1.5.6 # homeassistant.components.emulated_hue # homeassistant.components.http