From 39d5fb82e5289f6c4e95d01d77c964987d84cad1 Mon Sep 17 00:00:00 2001 From: bsmappee <58250533+bsmappee@users.noreply.github.com> Date: Thu, 9 Jul 2020 13:03:23 +0200 Subject: [PATCH 01/11] Smappee dependency update (#37680) --- homeassistant/components/smappee/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/smappee/manifest.json b/homeassistant/components/smappee/manifest.json index 1c760376e04391..4f9a33b74dafa9 100644 --- a/homeassistant/components/smappee/manifest.json +++ b/homeassistant/components/smappee/manifest.json @@ -5,7 +5,7 @@ "documentation": "https://www.home-assistant.io/integrations/smappee", "dependencies": ["http"], "requirements": [ - "pysmappee==0.1.4" + "pysmappee==0.1.5" ], "codeowners": [ "@bsmappee" diff --git a/requirements_all.txt b/requirements_all.txt index 37d159549be739..bfd590f3171c41 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1610,7 +1610,7 @@ pysignalclirestapi==0.3.4 pysma==0.3.5 # homeassistant.components.smappee -pysmappee==0.1.4 +pysmappee==0.1.5 # homeassistant.components.smartthings pysmartapp==0.3.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e12244eac93216..1687252fe27b40 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -715,7 +715,7 @@ pysignalclirestapi==0.3.4 pysma==0.3.5 # homeassistant.components.smappee -pysmappee==0.1.4 +pysmappee==0.1.5 # homeassistant.components.smartthings pysmartapp==0.3.2 From 0d58048cea3f835f53b8dbf7019ca3bee48b8909 Mon Sep 17 00:00:00 2001 From: Rami Mosleh Date: Mon, 13 Jul 2020 22:32:22 +0300 Subject: [PATCH 02/11] Properly set update_interval during Speedtest setup (#37708) * Properly set update_interval during setup * implement new update_interval method --- .../components/speedtestdotnet/__init__.py | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/speedtestdotnet/__init__.py b/homeassistant/components/speedtestdotnet/__init__.py index 1b50516f340aa3..2ef498770315be 100644 --- a/homeassistant/components/speedtestdotnet/__init__.py +++ b/homeassistant/components/speedtestdotnet/__init__.py @@ -105,16 +105,14 @@ def __init__(self, hass, config_entry): self.api = None self.servers = {} super().__init__( - self.hass, - _LOGGER, - name=DOMAIN, - update_method=self.async_update, - update_interval=timedelta( + self.hass, _LOGGER, name=DOMAIN, update_method=self.async_update, + ) + if not self.config_entry.options.get(CONF_MANUAL): + self.update_interval = timedelta( minutes=self.config_entry.options.get( CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL ) - ), - ) + ) def update_servers(self): """Update list of test servers.""" @@ -189,12 +187,11 @@ async def request_update(call): async def options_updated_listener(hass, entry): """Handle options update.""" - if not entry.options[CONF_MANUAL]: - hass.data[DOMAIN].update_interval = timedelta( - minutes=entry.options[CONF_SCAN_INTERVAL] - ) - await hass.data[DOMAIN].async_request_refresh() + if entry.options[CONF_MANUAL]: + hass.data[DOMAIN].update_interval = None return - # set the update interval to a very long time - # if the user wants to disable auto update - hass.data[DOMAIN].update_interval = timedelta(days=7) + + hass.data[DOMAIN].update_interval = timedelta( + minutes=entry.options[CONF_SCAN_INTERVAL] + ) + await hass.data[DOMAIN].async_request_refresh() From 5d26f5d01d2820fce9ab2be0637060e7fbbc3c66 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sat, 4 Jul 2020 17:48:34 +0200 Subject: [PATCH 03/11] Support multiple MQTT availability topics (#37418) * Support multiple MQTT availability topics * Make availability list and availability_topic exclusive * Make availability list and availability_topic exclusive * Add missing abbreviation --- homeassistant/components/mqtt/__init__.py | 79 ++++++-- .../components/mqtt/abbreviations.py | 1 + tests/components/mqtt/test_common.py | 174 ++++++++++++++++++ tests/components/mqtt/test_sensor.py | 24 +++ 4 files changed, 260 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 5f82fa76dfa9e3..b743626ebbb5bb 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -88,6 +88,8 @@ CONF_TLS_VERSION = "tls_version" CONF_COMMAND_TOPIC = "command_topic" +CONF_TOPIC = "topic" +CONF_AVAILABILITY = "availability" CONF_AVAILABILITY_TOPIC = "availability_topic" CONF_PAYLOAD_AVAILABLE = "payload_available" CONF_PAYLOAD_NOT_AVAILABLE = "payload_not_available" @@ -203,9 +205,9 @@ def embedded_broker_deprecated(value): SCHEMA_BASE = {vol.Optional(CONF_QOS, default=DEFAULT_QOS): _VALID_QOS_SCHEMA} -MQTT_AVAILABILITY_SCHEMA = vol.Schema( +MQTT_AVAILABILITY_SINGLE_SCHEMA = vol.Schema( { - vol.Optional(CONF_AVAILABILITY_TOPIC): valid_subscribe_topic, + vol.Exclusive(CONF_AVAILABILITY_TOPIC, "availability"): valid_subscribe_topic, vol.Optional( CONF_PAYLOAD_AVAILABLE, default=DEFAULT_PAYLOAD_AVAILABLE ): cv.string, @@ -215,6 +217,30 @@ def embedded_broker_deprecated(value): } ) +MQTT_AVAILABILITY_LIST_SCHEMA = vol.Schema( + { + vol.Exclusive(CONF_AVAILABILITY, "availability"): vol.All( + cv.ensure_list, + [ + { + vol.Optional(CONF_TOPIC): valid_subscribe_topic, + vol.Optional( + CONF_PAYLOAD_AVAILABLE, default=DEFAULT_PAYLOAD_AVAILABLE + ): cv.string, + vol.Optional( + CONF_PAYLOAD_NOT_AVAILABLE, + default=DEFAULT_PAYLOAD_NOT_AVAILABLE, + ): cv.string, + } + ], + ), + } +) + +MQTT_AVAILABILITY_SCHEMA = MQTT_AVAILABILITY_SINGLE_SCHEMA.extend( + MQTT_AVAILABILITY_LIST_SCHEMA.schema +) + MQTT_ENTITY_DEVICE_INFO_SCHEMA = vol.All( cv.deprecated(CONF_DEPRECATED_VIA_HUB, CONF_VIA_DEVICE), vol.Schema( @@ -989,8 +1015,7 @@ def __init__(self, config: dict) -> None: """Initialize the availability mixin.""" self._availability_sub_state = None self._available = False - - self._avail_config = config + self._availability_setup_from_config(config) async def async_added_to_hass(self) -> None: """Subscribe MQTT events.""" @@ -1004,9 +1029,27 @@ async def async_added_to_hass(self) -> None: async def availability_discovery_update(self, config: dict): """Handle updated discovery message.""" - self._avail_config = config + self._availability_setup_from_config(config) await self._availability_subscribe_topics() + def _availability_setup_from_config(self, config): + """(Re)Setup.""" + self._avail_topics = {} + if CONF_AVAILABILITY_TOPIC in config: + self._avail_topics[config[CONF_AVAILABILITY_TOPIC]] = { + CONF_PAYLOAD_AVAILABLE: config[CONF_PAYLOAD_AVAILABLE], + CONF_PAYLOAD_NOT_AVAILABLE: config[CONF_PAYLOAD_NOT_AVAILABLE], + } + + if CONF_AVAILABILITY in config: + for avail in config[CONF_AVAILABILITY]: + self._avail_topics[avail[CONF_TOPIC]] = { + CONF_PAYLOAD_AVAILABLE: avail[CONF_PAYLOAD_AVAILABLE], + CONF_PAYLOAD_NOT_AVAILABLE: avail[CONF_PAYLOAD_NOT_AVAILABLE], + } + + self._avail_config = config + async def _availability_subscribe_topics(self): """(Re)Subscribe to topics.""" @@ -1014,23 +1057,24 @@ async def _availability_subscribe_topics(self): @log_messages(self.hass, self.entity_id) def availability_message_received(msg: Message) -> None: """Handle a new received MQTT availability message.""" - if msg.payload == self._avail_config[CONF_PAYLOAD_AVAILABLE]: + topic = msg.topic + if msg.payload == self._avail_topics[topic][CONF_PAYLOAD_AVAILABLE]: self._available = True - elif msg.payload == self._avail_config[CONF_PAYLOAD_NOT_AVAILABLE]: + elif msg.payload == self._avail_topics[topic][CONF_PAYLOAD_NOT_AVAILABLE]: self._available = False self.async_write_ha_state() + topics = {} + for topic in self._avail_topics: + topics[f"availability_{topic}"] = { + "topic": topic, + "msg_callback": availability_message_received, + "qos": self._avail_config[CONF_QOS], + } + self._availability_sub_state = await async_subscribe_topics( - self.hass, - self._availability_sub_state, - { - "availability_topic": { - "topic": self._avail_config.get(CONF_AVAILABILITY_TOPIC), - "msg_callback": availability_message_received, - "qos": self._avail_config[CONF_QOS], - } - }, + self.hass, self._availability_sub_state, topics, ) @callback @@ -1048,10 +1092,9 @@ async def async_will_remove_from_hass(self): @property def available(self) -> bool: """Return if the device is available.""" - availability_topic = self._avail_config.get(CONF_AVAILABILITY_TOPIC) if not self.hass.data[DATA_MQTT].connected: return False - return availability_topic is None or self._available + return not self._avail_topics or self._available async def cleanup_device_registry(hass, device_id): diff --git a/homeassistant/components/mqtt/abbreviations.py b/homeassistant/components/mqtt/abbreviations.py index 2ec0ea0d203865..c3f6b55e0fed3c 100644 --- a/homeassistant/components/mqtt/abbreviations.py +++ b/homeassistant/components/mqtt/abbreviations.py @@ -7,6 +7,7 @@ "aux_cmd_t": "aux_command_topic", "aux_stat_tpl": "aux_state_template", "aux_stat_t": "aux_state_topic", + "avty": "availability", "avty_t": "availability_topic", "away_mode_cmd_t": "away_mode_command_topic", "away_mode_stat_tpl": "away_mode_state_template", diff --git a/tests/components/mqtt/test_common.py b/tests/components/mqtt/test_common.py index 4275cc36e134f8..31566885a373cc 100644 --- a/tests/components/mqtt/test_common.py +++ b/tests/components/mqtt/test_common.py @@ -104,6 +104,98 @@ async def help_test_default_availability_payload( assert state.state != STATE_UNAVAILABLE +async def help_test_default_availability_list_payload( + hass, + mqtt_mock, + domain, + config, + no_assumed_state=False, + state_topic=None, + state_message=None, +): + """Test availability by default payload with defined topic. + + This is a test helper for the MqttAvailability mixin. + """ + # Add availability settings to config + config = copy.deepcopy(config) + config[domain]["availability"] = [ + {"topic": "availability-topic1"}, + {"topic": "availability-topic2"}, + ] + assert await async_setup_component(hass, domain, config,) + await hass.async_block_till_done() + + state = hass.states.get(f"{domain}.test") + assert state.state == STATE_UNAVAILABLE + + async_fire_mqtt_message(hass, "availability-topic1", "online") + + state = hass.states.get(f"{domain}.test") + assert state.state != STATE_UNAVAILABLE + if no_assumed_state: + assert not state.attributes.get(ATTR_ASSUMED_STATE) + + async_fire_mqtt_message(hass, "availability-topic1", "offline") + + state = hass.states.get(f"{domain}.test") + assert state.state == STATE_UNAVAILABLE + + async_fire_mqtt_message(hass, "availability-topic2", "online") + + state = hass.states.get(f"{domain}.test") + assert state.state != STATE_UNAVAILABLE + if no_assumed_state: + assert not state.attributes.get(ATTR_ASSUMED_STATE) + + async_fire_mqtt_message(hass, "availability-topic2", "offline") + + state = hass.states.get(f"{domain}.test") + assert state.state == STATE_UNAVAILABLE + + if state_topic: + async_fire_mqtt_message(hass, state_topic, state_message) + + state = hass.states.get(f"{domain}.test") + assert state.state == STATE_UNAVAILABLE + + async_fire_mqtt_message(hass, "availability-topic1", "online") + + state = hass.states.get(f"{domain}.test") + assert state.state != STATE_UNAVAILABLE + + +async def help_test_default_availability_list_single( + hass, + mqtt_mock, + caplog, + domain, + config, + no_assumed_state=False, + state_topic=None, + state_message=None, +): + """Test availability list and availability_topic are mutually exclusive. + + This is a test helper for the MqttAvailability mixin. + """ + # Add availability settings to config + config = copy.deepcopy(config) + config[domain]["availability"] = [ + {"topic": "availability-topic1"}, + ] + config[domain]["availability_topic"] = "availability-topic" + assert await async_setup_component(hass, domain, config,) + await hass.async_block_till_done() + + state = hass.states.get(f"{domain}.test") + assert state is None + assert ( + "Invalid config for [sensor.mqtt]: two or more values in the same group of exclusion 'availability'" + in caplog.text + ) + + async def help_test_custom_availability_payload( hass, mqtt_mock, @@ -152,6 +244,88 @@ async def help_test_custom_availability_payload( assert state.state != STATE_UNAVAILABLE +async def help_test_discovery_update_availability( + hass, + mqtt_mock, + domain, + config, + no_assumed_state=False, + state_topic=None, + state_message=None, +): + """Test update of discovered MQTTAvailability. + + This is a test helper for the MQTTAvailability mixin. + """ + # Add availability settings to config + config1 = copy.deepcopy(config) + config1[domain]["availability_topic"] = "availability-topic1" + config2 = copy.deepcopy(config) + config2[domain]["availability"] = [ + {"topic": "availability-topic2"}, + {"topic": "availability-topic3"}, + ] + config3 = copy.deepcopy(config) + config3[domain]["availability_topic"] = "availability-topic4" + data1 = json.dumps(config1[domain]) + data2 = json.dumps(config2[domain]) + data3 = json.dumps(config3[domain]) + + entry = hass.config_entries.async_entries(mqtt.DOMAIN)[0] + await async_start(hass, "homeassistant", entry) + async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data1) + await hass.async_block_till_done() + + state = hass.states.get(f"{domain}.test") + assert state.state == STATE_UNAVAILABLE + + async_fire_mqtt_message(hass, "availability-topic1", "online") + state = hass.states.get(f"{domain}.test") + assert state.state != STATE_UNAVAILABLE + + async_fire_mqtt_message(hass, "availability-topic1", "offline") + state = hass.states.get(f"{domain}.test") + assert state.state == STATE_UNAVAILABLE + + # Change availability_topic + async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data2) + await hass.async_block_till_done() + + # Verify we are no longer subscribing to the old topic + async_fire_mqtt_message(hass, "availability-topic1", "online") + state = hass.states.get(f"{domain}.test") + assert state.state == STATE_UNAVAILABLE + + # Verify we are subscribing to the new topic + async_fire_mqtt_message(hass, "availability-topic2", "online") + state = hass.states.get(f"{domain}.test") + assert state.state != STATE_UNAVAILABLE + + # Verify we are subscribing to the new topic + async_fire_mqtt_message(hass, "availability-topic3", "offline") + state = hass.states.get(f"{domain}.test") + assert state.state == STATE_UNAVAILABLE + + # Change availability_topic + async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data3) + await hass.async_block_till_done() + + # Verify we are no longer subscribing to the old topic + async_fire_mqtt_message(hass, "availability-topic2", "online") + state = hass.states.get(f"{domain}.test") + assert state.state == STATE_UNAVAILABLE + + # Verify we are no longer subscribing to the old topic + async_fire_mqtt_message(hass, "availability-topic3", "online") + state = hass.states.get(f"{domain}.test") + assert state.state == STATE_UNAVAILABLE + + # Verify we are subscribing to the new topic + async_fire_mqtt_message(hass, "availability-topic4", "online") + state = hass.states.get(f"{domain}.test") + assert state.state != STATE_UNAVAILABLE + + async def help_test_setting_attribute_via_mqtt_json_message( hass, mqtt_mock, domain, config ): diff --git a/tests/components/mqtt/test_sensor.py b/tests/components/mqtt/test_sensor.py index f54a27e88052d1..4d1209614eba23 100644 --- a/tests/components/mqtt/test_sensor.py +++ b/tests/components/mqtt/test_sensor.py @@ -16,11 +16,14 @@ help_test_availability_when_connection_lost, help_test_availability_without_topic, help_test_custom_availability_payload, + help_test_default_availability_list_payload, + help_test_default_availability_list_single, help_test_default_availability_payload, help_test_discovery_broken, help_test_discovery_removal, help_test_discovery_update, help_test_discovery_update_attr, + help_test_discovery_update_availability, help_test_entity_debug_info, help_test_entity_debug_info_max_messages, help_test_entity_debug_info_message, @@ -250,6 +253,20 @@ async def test_default_availability_payload(hass, mqtt_mock): ) +async def test_default_availability_list_payload(hass, mqtt_mock): + """Test availability by default payload with defined topic.""" + await help_test_default_availability_list_payload( + hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + ) + + +async def test_default_availability_list_single(hass, mqtt_mock, caplog): + """Test availability list and availability_topic are mutually exclusive.""" + await help_test_default_availability_list_single( + hass, mqtt_mock, caplog, sensor.DOMAIN, DEFAULT_CONFIG + ) + + async def test_custom_availability_payload(hass, mqtt_mock): """Test availability by custom payload with defined topic.""" await help_test_custom_availability_payload( @@ -257,6 +274,13 @@ async def test_custom_availability_payload(hass, mqtt_mock): ) +async def test_discovery_update_availability(hass, mqtt_mock): + """Test availability discovery update.""" + await help_test_discovery_update_availability( + hass, mqtt_mock, sensor.DOMAIN, DEFAULT_CONFIG + ) + + async def test_invalid_device_class(hass, mqtt_mock): """Test device_class option with invalid value.""" assert await async_setup_component( From 114fbb1278e9391ca32e7c71a68ad77c5d3f8670 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 10 Jul 2020 18:39:18 +0200 Subject: [PATCH 04/11] Tweak MQTT availability (#37719) --- homeassistant/components/mqtt/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index b743626ebbb5bb..5742281f89d495 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -1021,11 +1021,14 @@ async def async_added_to_hass(self) -> None: """Subscribe MQTT events.""" await super().async_added_to_hass() await self._availability_subscribe_topics() - async_dispatcher_connect(self.hass, MQTT_CONNECTED, self.async_mqtt_connect) - async_dispatcher_connect(self.hass, MQTT_DISCONNECTED, self.async_mqtt_connect) self.async_on_remove( async_dispatcher_connect(self.hass, MQTT_CONNECTED, self.async_mqtt_connect) ) + self.async_on_remove( + async_dispatcher_connect( + self.hass, MQTT_DISCONNECTED, self.async_mqtt_connect + ) + ) async def availability_discovery_update(self, config: dict): """Handle updated discovery message.""" @@ -1092,7 +1095,7 @@ async def async_will_remove_from_hass(self): @property def available(self) -> bool: """Return if the device is available.""" - if not self.hass.data[DATA_MQTT].connected: + if not self.hass.data[DATA_MQTT].connected and not self.hass.is_stopping: return False return not self._avail_topics or self._available From 502f0cd2cea7b5f78960174583eb91a217c3a8af Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 10 Jul 2020 18:40:15 +0200 Subject: [PATCH 05/11] Fix MQTT availability startup race (#37718) --- homeassistant/components/mqtt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 5742281f89d495..49ac83b21620da 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -1083,7 +1083,7 @@ def availability_message_received(msg: Message) -> None: @callback def async_mqtt_connect(self): """Update state on connection/disconnection to MQTT broker.""" - if self.hass.is_running: + if not self.hass.is_stopping: self.async_write_ha_state() async def async_will_remove_from_hass(self): From 6eca0b2a399b29080be2ad31b919b12bbde4a809 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Jul 2020 14:45:05 -1000 Subject: [PATCH 06/11] Fix homekit_controller discovery via zeroconf (#37725) --- homeassistant/components/zeroconf/__init__.py | 5 +++-- tests/components/zeroconf/test_init.py | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/zeroconf/__init__.py b/homeassistant/components/zeroconf/__init__.py index 9f0d203d2b3118..084cc9c21866fc 100644 --- a/homeassistant/components/zeroconf/__init__.py +++ b/homeassistant/components/zeroconf/__init__.py @@ -200,7 +200,7 @@ def service_update(zeroconf, service_type, name, state_change): # If we can handle it as a HomeKit discovery, we do that here. if service_type == HOMEKIT_TYPE: - handle_homekit(hass, info) + discovery_was_forwarded = handle_homekit(hass, info) # Continue on here as homekit_controller # still needs to get updates on devices # so it can see when the 'c#' field is updated. @@ -209,7 +209,8 @@ def service_update(zeroconf, service_type, name, state_change): # if the device is already paired in order to avoid # offering a second discovery for the same device if ( - HOMEKIT_PROPERTIES in info + discovery_was_forwarded + and HOMEKIT_PROPERTIES in info and HOMEKIT_PAIRED_STATUS_FLAG in info[HOMEKIT_PROPERTIES] ): try: diff --git a/tests/components/zeroconf/test_init.py b/tests/components/zeroconf/test_init.py index 45b1d9b1171df4..dcde788e0507ae 100644 --- a/tests/components/zeroconf/test_init.py +++ b/tests/components/zeroconf/test_init.py @@ -215,6 +215,25 @@ async def test_homekit_invalid_paring_status(hass, mock_zeroconf): assert mock_config_flow.mock_calls[0][1][0] == "tado" +async def test_homekit_not_paired(hass, mock_zeroconf): + """Test that an not paired device is sent to homekit_controller.""" + with patch.dict( + zc_gen.ZEROCONF, {zeroconf.HOMEKIT_TYPE: ["homekit_controller"]}, clear=True + ), patch.object( + hass.config_entries.flow, "async_init" + ) as mock_config_flow, patch.object( + zeroconf, "HaServiceBrowser", side_effect=service_update_mock + ) as mock_service_browser: + mock_zeroconf.get_service_info.side_effect = get_homekit_info_mock( + "this_will_not_match_any_integration", HOMEKIT_STATUS_UNPAIRED + ) + assert await async_setup_component(hass, zeroconf.DOMAIN, {zeroconf.DOMAIN: {}}) + + assert len(mock_service_browser.mock_calls) == 1 + assert len(mock_config_flow.mock_calls) == 1 + assert mock_config_flow.mock_calls[0][1][0] == "homekit_controller" + + async def test_info_from_service_non_utf8(hass): """Test info_from_service handles non UTF-8 property keys and values correctly.""" service_type = "_test._tcp.local." From d0e26c3deee437cb19dd7777f9aededf91b1acb1 Mon Sep 17 00:00:00 2001 From: Rohan Kapoor Date: Fri, 10 Jul 2020 15:48:20 -0700 Subject: [PATCH 07/11] Add support for the DataUpdateCoordinator to not automatically update (#37734) --- homeassistant/helpers/update_coordinator.py | 5 +- tests/helpers/test_update_coordinator.py | 67 +++++++++++++++++++-- 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/homeassistant/helpers/update_coordinator.py b/homeassistant/helpers/update_coordinator.py index 7fb96fdf7c844f..5998d428d6a41a 100644 --- a/homeassistant/helpers/update_coordinator.py +++ b/homeassistant/helpers/update_coordinator.py @@ -30,7 +30,7 @@ def __init__( logger: logging.Logger, *, name: str, - update_interval: timedelta, + update_interval: Optional[timedelta] = None, update_method: Optional[Callable[[], Awaitable]] = None, request_refresh_debouncer: Optional[Debouncer] = None, ): @@ -91,6 +91,9 @@ def async_remove_listener(self, update_callback: CALLBACK_TYPE) -> None: @callback def _schedule_refresh(self) -> None: """Schedule a refresh.""" + if self.update_interval is None: + return + if self._unsub_refresh: self._unsub_refresh() self._unsub_refresh = None diff --git a/tests/helpers/test_update_coordinator.py b/tests/helpers/test_update_coordinator.py index 8d4f6934d78bab..99399fee30f602 100644 --- a/tests/helpers/test_update_coordinator.py +++ b/tests/helpers/test_update_coordinator.py @@ -15,9 +15,8 @@ LOGGER = logging.getLogger(__name__) -@pytest.fixture -def crd(hass): - """Coordinator mock.""" +def get_crd(hass, update_interval): + """Make coordinator mocks.""" calls = 0 async def refresh(): @@ -30,11 +29,26 @@ async def refresh(): LOGGER, name="test", update_method=refresh, - update_interval=timedelta(seconds=10), + update_interval=update_interval, ) return crd +DEFAULT_UPDATE_INTERVAL = timedelta(seconds=10) + + +@pytest.fixture +def crd(hass): + """Coordinator mock with default update interval.""" + return get_crd(hass, DEFAULT_UPDATE_INTERVAL) + + +@pytest.fixture +def crd_without_update_interval(hass): + """Coordinator mock that never automatically updates.""" + return get_crd(hass, None) + + async def test_async_refresh(crd): """Test async_refresh for update coordinator.""" assert crd.data is None @@ -79,6 +93,20 @@ async def test_request_refresh(crd): assert crd.last_update_success is True +async def test_request_refresh_no_auto_update(crd_without_update_interval): + """Test request refresh for update coordinator without automatic update.""" + crd = crd_without_update_interval + assert crd.data is None + await crd.async_request_refresh() + assert crd.data == 1 + assert crd.last_update_success is True + + # Second time we hit the debonuce + await crd.async_request_refresh() + assert crd.data == 1 + assert crd.last_update_success is True + + @pytest.mark.parametrize( "err_msg", [ @@ -151,6 +179,37 @@ async def test_update_interval(hass, crd): assert crd.data == 2 +async def test_update_interval_not_present(hass, crd_without_update_interval): + """Test update never happens with no update interval.""" + crd = crd_without_update_interval + # Test we don't update without subscriber with no update interval + async_fire_time_changed(hass, utcnow() + DEFAULT_UPDATE_INTERVAL) + await hass.async_block_till_done() + assert crd.data is None + + # Add subscriber + update_callback = Mock() + crd.async_add_listener(update_callback) + + # Test twice we don't update with subscriber with no update interval + async_fire_time_changed(hass, utcnow() + DEFAULT_UPDATE_INTERVAL) + await hass.async_block_till_done() + assert crd.data is None + + async_fire_time_changed(hass, utcnow() + DEFAULT_UPDATE_INTERVAL) + await hass.async_block_till_done() + assert crd.data is None + + # Test removing listener + crd.async_remove_listener(update_callback) + + async_fire_time_changed(hass, utcnow() + DEFAULT_UPDATE_INTERVAL) + await hass.async_block_till_done() + + # Test we stop don't update after we lose last subscriber + assert crd.data is None + + async def test_refresh_recover(crd, caplog): """Test recovery of freshing data.""" crd.last_update_success = False From 2c58d860b6ea7f6a73dd91087e5652b33b027474 Mon Sep 17 00:00:00 2001 From: Kevin Fronczak Date: Sat, 11 Jul 2020 11:50:18 -0400 Subject: [PATCH 08/11] Bump blinkpy version to fix connection errors (#37755) * Bump blinkpy version to fix connection errors * Bump blinkpy version to fix connection errors * Rebased, re-ran gen_requirements_all --- homeassistant/components/blink/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/blink/manifest.json b/homeassistant/components/blink/manifest.json index ac42870fdb79e7..a42763e584338b 100644 --- a/homeassistant/components/blink/manifest.json +++ b/homeassistant/components/blink/manifest.json @@ -2,7 +2,7 @@ "domain": "blink", "name": "Blink", "documentation": "https://www.home-assistant.io/integrations/blink", - "requirements": ["blinkpy==0.15.0"], + "requirements": ["blinkpy==0.15.1"], "codeowners": ["@fronzbot"], "config_flow": true } diff --git a/requirements_all.txt b/requirements_all.txt index bfd590f3171c41..2ffa1bf78694b6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -348,7 +348,7 @@ bizkaibus==0.1.1 blebox_uniapi==1.3.2 # homeassistant.components.blink -blinkpy==0.15.0 +blinkpy==0.15.1 # homeassistant.components.blinksticklight blinkstick==1.1.8 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1687252fe27b40..5fb080310a325c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -171,7 +171,7 @@ bellows==0.17.0 blebox_uniapi==1.3.2 # homeassistant.components.blink -blinkpy==0.15.0 +blinkpy==0.15.1 # homeassistant.components.bom bomradarloop==0.1.4 From 738d3a13e193ecae7e200bb27092bade9b71cfbb Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Sun, 12 Jul 2020 09:47:26 +0200 Subject: [PATCH 09/11] UniFi - Handle session expiration (#37782) --- homeassistant/components/unifi/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/unifi/manifest.json b/homeassistant/components/unifi/manifest.json index 124c7241c30150..9c1896f0c481b6 100644 --- a/homeassistant/components/unifi/manifest.json +++ b/homeassistant/components/unifi/manifest.json @@ -3,7 +3,7 @@ "name": "Ubiquiti UniFi", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/unifi", - "requirements": ["aiounifi==22"], + "requirements": ["aiounifi==23"], "codeowners": ["@Kane610"], "quality_scale": "platinum" } diff --git a/requirements_all.txt b/requirements_all.txt index 2ffa1bf78694b6..d1476a89d6957c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -230,7 +230,7 @@ aiopylgtv==0.3.3 aioswitcher==1.2.0 # homeassistant.components.unifi -aiounifi==22 +aiounifi==23 # homeassistant.components.airly airly==0.0.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5fb080310a325c..b2b6008d3981f0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -122,7 +122,7 @@ aiopylgtv==0.3.3 aioswitcher==1.2.0 # homeassistant.components.unifi -aiounifi==22 +aiounifi==23 # homeassistant.components.airly airly==0.0.2 From 5d1d113a25b87c9f1e66ece3d7060d3b2cf76e67 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Mon, 13 Jul 2020 01:16:40 +0200 Subject: [PATCH 10/11] deCONZ - don't let light "attr" events update group data (#37797) --- homeassistant/components/deconz/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/deconz/manifest.json b/homeassistant/components/deconz/manifest.json index 149ea5f1bc5d82..2cba87f74d6e0f 100644 --- a/homeassistant/components/deconz/manifest.json +++ b/homeassistant/components/deconz/manifest.json @@ -3,7 +3,7 @@ "name": "deCONZ", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/deconz", - "requirements": ["pydeconz==71"], + "requirements": ["pydeconz==72"], "ssdp": [ { "manufacturer": "Royal Philips Electronics" diff --git a/requirements_all.txt b/requirements_all.txt index d1476a89d6957c..d9d277f19cf0df 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1282,7 +1282,7 @@ pydaikin==2.2.0 pydanfossair==0.1.0 # homeassistant.components.deconz -pydeconz==71 +pydeconz==72 # homeassistant.components.delijn pydelijn==0.6.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b2b6008d3981f0..08a3ac61619edc 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -576,7 +576,7 @@ pycountry==19.8.18 pydaikin==2.2.0 # homeassistant.components.deconz -pydeconz==71 +pydeconz==72 # homeassistant.components.zwave pydispatcher==2.0.5 From fb484e87c0b70cffa26a1d05189842d1170db6e3 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 14 Jul 2020 01:11:49 +0000 Subject: [PATCH 11/11] Bumped version to 0.112.5 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 47f271789e2da5..d490e4c090b453 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 0 MINOR_VERSION = 112 -PATCH_VERSION = "4" +PATCH_VERSION = "5" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 0)