From 996a3477b0a64abb6ac9f3a7c2e746d829ccf263 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 8 Sep 2022 12:53:30 -0400 Subject: [PATCH 01/15] Increase rate limit for zwave_js updates Al provided a new key which bumps the rate limit from 10k per hour to 100k per hour --- homeassistant/components/zwave_js/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/zwave_js/const.py b/homeassistant/components/zwave_js/const.py index db3da247e7d..ff1a97d6ecc 100644 --- a/homeassistant/components/zwave_js/const.py +++ b/homeassistant/components/zwave_js/const.py @@ -123,5 +123,5 @@ ENTITY_DESC_KEY_TOTAL_INCREASING = "total_increasing" # This API key is only for use with Home Assistant. Reach out to Z-Wave JS to apply for # your own (https://github.com/zwave-js/firmware-updates/). API_KEY_FIRMWARE_UPDATE_SERVICE = ( - "55eea74f055bef2ad893348112df6a38980600aaf82d2b02011297fc7ba495f830ca2b70" + "2e39d98fc56386389fbb35e5a98fa1b44b9fdd8f971460303587cff408430d4cfcde6134" ) From 3fd887b1f2b52da3642a773e1b27d4027cecc381 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 8 Sep 2022 16:40:55 -0400 Subject: [PATCH 02/15] Show progress for zwave_js.update entity (#77905) Co-authored-by: Martin Hjelmare --- homeassistant/components/zwave_js/update.py | 68 ++++++++++++++++++--- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/zwave_js/update.py b/homeassistant/components/zwave_js/update.py index 97c14746dd9..f5604c8280c 100644 --- a/homeassistant/components/zwave_js/update.py +++ b/homeassistant/components/zwave_js/update.py @@ -4,6 +4,7 @@ from __future__ import annotations import asyncio from collections.abc import Callable from datetime import datetime, timedelta +from math import floor from typing import Any from awesomeversion import AwesomeVersion @@ -11,7 +12,7 @@ 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 +from zwave_js_server.model.firmware import FirmwareUpdateInfo, FirmwareUpdateProgress from zwave_js_server.model.node import Node as ZwaveNode from homeassistant.components.update import UpdateDeviceClass, UpdateEntity @@ -63,7 +64,9 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity): _attr_entity_category = EntityCategory.CONFIG _attr_device_class = UpdateDeviceClass.FIRMWARE _attr_supported_features = ( - UpdateEntityFeature.INSTALL | UpdateEntityFeature.RELEASE_NOTES + UpdateEntityFeature.INSTALL + | UpdateEntityFeature.RELEASE_NOTES + | UpdateEntityFeature.PROGRESS ) _attr_has_entity_name = True _attr_should_poll = False @@ -78,6 +81,8 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity): self._latest_version_firmware: FirmwareUpdateInfo | None = None self._status_unsub: Callable[[], None] | None = None self._poll_unsub: Callable[[], None] | None = None + self._progress_unsub: Callable[[], None] | None = None + self._num_files_installed: int = 0 # Entity class attributes self._attr_name = "Firmware" @@ -93,6 +98,36 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity): self._status_unsub = None self.hass.async_create_task(self._async_update()) + @callback + def _update_progress(self, event: dict[str, Any]) -> None: + """Update install progress on event.""" + progress: FirmwareUpdateProgress = event["firmware_update_progress"] + if not self._latest_version_firmware: + return + # We will assume that each file in the firmware update represents an equal + # percentage of the overall progress. This is likely not true because each file + # may be a different size, but it's the best we can do since we don't know the + # total number of fragments across all files. + self._attr_in_progress = floor( + 100 + * ( + self._num_files_installed + + (progress.sent_fragments / progress.total_fragments) + ) + / len(self._latest_version_firmware.files) + ) + self.async_write_ha_state() + + @callback + def _reset_progress(self) -> None: + """Reset update install progress.""" + if self._progress_unsub: + self._progress_unsub() + self._progress_unsub = None + self._num_files_installed = 0 + self._attr_in_progress = False + self.async_write_ha_state() + async def _async_update(self, _: HomeAssistant | datetime | None = None) -> None: """Update the entity.""" self._poll_unsub = None @@ -152,18 +187,29 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity): """Install an update.""" firmware = self._latest_version_firmware assert firmware - try: - for file in firmware.files: + self._attr_in_progress = 0 + self.async_write_ha_state() + self._progress_unsub = self.node.on( + "firmware update progress", self._update_progress + ) + for file in firmware.files: + try: await self.driver.controller.async_begin_ota_firmware_update( self.node, file ) - except BaseZwaveJSServerError as err: - raise HomeAssistantError(err) from err - else: - self._attr_installed_version = self._attr_latest_version = firmware.version - self._latest_version_firmware = None + except BaseZwaveJSServerError as err: + self._reset_progress() + raise HomeAssistantError(err) from err + self._num_files_installed += 1 + self._attr_in_progress = floor( + 100 * self._num_files_installed / len(firmware.files) + ) self.async_write_ha_state() + self._attr_installed_version = self._attr_latest_version = firmware.version + self._latest_version_firmware = None + self._reset_progress() + async def async_poll_value(self, _: bool) -> None: """Poll a value.""" LOGGER.error( @@ -200,3 +246,7 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity): if self._poll_unsub: self._poll_unsub() self._poll_unsub = None + + if self._progress_unsub: + self._progress_unsub() + self._progress_unsub = None From ce6d337bd51d4f99cbdfabd0ece92afa6bdd9557 Mon Sep 17 00:00:00 2001 From: Yevhenii Vaskivskyi Date: Thu, 8 Sep 2022 08:49:36 +0200 Subject: [PATCH 03/15] Fix `len` method typo for Osram light (#78008) * Fix `len` method typo for Osram light * Fix typo in line 395 --- homeassistant/components/osramlightify/light.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/osramlightify/light.py b/homeassistant/components/osramlightify/light.py index 9fc54d0352c..e1b204115e6 100644 --- a/homeassistant/components/osramlightify/light.py +++ b/homeassistant/components/osramlightify/light.py @@ -373,7 +373,7 @@ class Luminary(LightEntity): self._max_mireds = color_util.color_temperature_kelvin_to_mired( self._luminary.min_temp() or DEFAULT_KELVIN ) - if len(self._attr_supported_color_modes == 1): + if len(self._attr_supported_color_modes) == 1: # The light supports only a single color mode self._attr_color_mode = list(self._attr_supported_color_modes)[0] @@ -392,7 +392,7 @@ class Luminary(LightEntity): if ColorMode.HS in self._attr_supported_color_modes: self._rgb_color = self._luminary.rgb() - if len(self._attr_supported_color_modes > 1): + if len(self._attr_supported_color_modes) > 1: # The light supports hs + color temp, determine which one it is if self._rgb_color == (0, 0, 0): self._attr_color_mode = ColorMode.COLOR_TEMP From ab9d9d599e6695d3b5dbad7215830cfb57b8ed9d Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Thu, 8 Sep 2022 03:13:01 -0400 Subject: [PATCH 04/15] Add value ID to zwave_js device diagnostics (#78015) --- homeassistant/components/zwave_js/diagnostics.py | 1 + tests/components/zwave_js/test_diagnostics.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/homeassistant/components/zwave_js/diagnostics.py b/homeassistant/components/zwave_js/diagnostics.py index 078bd761b71..33d32e96fe0 100644 --- a/homeassistant/components/zwave_js/diagnostics.py +++ b/homeassistant/components/zwave_js/diagnostics.py @@ -123,6 +123,7 @@ def get_device_entities( "entity_category": entry.entity_category, "supported_features": entry.supported_features, "unit_of_measurement": entry.unit_of_measurement, + "value_id": value_id, "primary_value": primary_value_data, } entities.append(entity) diff --git a/tests/components/zwave_js/test_diagnostics.py b/tests/components/zwave_js/test_diagnostics.py index 9f3a7b0884c..41505364111 100644 --- a/tests/components/zwave_js/test_diagnostics.py +++ b/tests/components/zwave_js/test_diagnostics.py @@ -152,6 +152,7 @@ async def test_device_diagnostics_missing_primary_value( x for x in diagnostics_data["entities"] if x["entity_id"] == entity_id ) + assert air_entity["value_id"] == value.value_id assert air_entity["primary_value"] == { "command_class": value.command_class, "command_class_name": value.command_class_name, @@ -189,4 +190,5 @@ async def test_device_diagnostics_missing_primary_value( x for x in diagnostics_data["entities"] if x["entity_id"] == entity_id ) + assert air_entity["value_id"] == value.value_id assert air_entity["primary_value"] is None From 31858ad7796fc42e3f91b97ccc288b476e5eb027 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Thu, 8 Sep 2022 10:59:40 +0200 Subject: [PATCH 05/15] Fix zwave_js default emulate hardware in options flow (#78024) --- .../components/zwave_js/config_flow.py | 4 ++- tests/components/zwave_js/test_config_flow.py | 30 +++++++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zwave_js/config_flow.py b/homeassistant/components/zwave_js/config_flow.py index 3da785cdcf2..c114662888f 100644 --- a/homeassistant/components/zwave_js/config_flow.py +++ b/homeassistant/components/zwave_js/config_flow.py @@ -792,7 +792,9 @@ class OptionsFlowHandler(BaseZwaveJSFlow, config_entries.OptionsFlow): CONF_ADDON_S2_AUTHENTICATED_KEY: self.s2_authenticated_key, CONF_ADDON_S2_UNAUTHENTICATED_KEY: self.s2_unauthenticated_key, CONF_ADDON_LOG_LEVEL: user_input[CONF_LOG_LEVEL], - CONF_ADDON_EMULATE_HARDWARE: user_input[CONF_EMULATE_HARDWARE], + CONF_ADDON_EMULATE_HARDWARE: user_input.get( + CONF_EMULATE_HARDWARE, False + ), } if new_addon_config != addon_config: diff --git a/tests/components/zwave_js/test_config_flow.py b/tests/components/zwave_js/test_config_flow.py index 6c4b18e8dc3..d4f159f2510 100644 --- a/tests/components/zwave_js/test_config_flow.py +++ b/tests/components/zwave_js/test_config_flow.py @@ -1951,6 +1951,30 @@ async def different_device_server_version(*args): 0, different_device_server_version, ), + ( + {"config": ADDON_DISCOVERY_INFO}, + {}, + { + "device": "/test", + "network_key": "old123", + "s0_legacy_key": "old123", + "s2_access_control_key": "old456", + "s2_authenticated_key": "old789", + "s2_unauthenticated_key": "old987", + "log_level": "info", + }, + { + "usb_path": "/new", + "s0_legacy_key": "new123", + "s2_access_control_key": "new456", + "s2_authenticated_key": "new789", + "s2_unauthenticated_key": "new987", + "log_level": "info", + "emulate_hardware": False, + }, + 0, + different_device_server_version, + ), ], ) async def test_options_different_device( @@ -2018,14 +2042,16 @@ async def test_options_different_device( result = await hass.config_entries.options.async_configure(result["flow_id"]) await hass.async_block_till_done() + # Default emulate_hardware is False. + addon_options = {"emulate_hardware": False} | old_addon_options # Legacy network key is not reset. - old_addon_options.pop("network_key") + addon_options.pop("network_key") assert set_addon_options.call_count == 2 assert set_addon_options.call_args == call( hass, "core_zwave_js", - {"options": old_addon_options}, + {"options": addon_options}, ) assert result["type"] == "progress" assert result["step_id"] == "start_addon" From 6f3b49601e4306bb0ce12ff4b250e84cf1dfa64c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Thu, 8 Sep 2022 10:50:14 +0200 Subject: [PATCH 06/15] Extract lametric device from coordinator in notify (#78027) --- homeassistant/components/lametric/notify.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/lametric/notify.py b/homeassistant/components/lametric/notify.py index 4b404840388..c9ae376c496 100644 --- a/homeassistant/components/lametric/notify.py +++ b/homeassistant/components/lametric/notify.py @@ -21,6 +21,7 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from .const import CONF_CYCLES, CONF_ICON_TYPE, CONF_PRIORITY, CONF_SOUND, DOMAIN +from .coordinator import LaMetricDataUpdateCoordinator async def async_get_service( @@ -31,8 +32,10 @@ async def async_get_service( """Get the LaMetric notification service.""" if discovery_info is None: return None - lametric: LaMetricDevice = hass.data[DOMAIN][discovery_info["entry_id"]] - return LaMetricNotificationService(lametric) + coordinator: LaMetricDataUpdateCoordinator = hass.data[DOMAIN][ + discovery_info["entry_id"] + ] + return LaMetricNotificationService(coordinator.lametric) class LaMetricNotificationService(BaseNotificationService): From 4009a32fb5f8e8e3013ce0b613c77fbe019b54ad Mon Sep 17 00:00:00 2001 From: Maikel Punie Date: Thu, 8 Sep 2022 20:28:40 +0200 Subject: [PATCH 07/15] Bump velbus-aio to 2022.9.1 (#78039) Bump velbusaio to 2022.9.1 --- homeassistant/components/velbus/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/velbus/manifest.json b/homeassistant/components/velbus/manifest.json index ec0c0f5f2d9..cbc8db0ca9f 100644 --- a/homeassistant/components/velbus/manifest.json +++ b/homeassistant/components/velbus/manifest.json @@ -2,7 +2,7 @@ "domain": "velbus", "name": "Velbus", "documentation": "https://www.home-assistant.io/integrations/velbus", - "requirements": ["velbus-aio==2022.6.2"], + "requirements": ["velbus-aio==2022.9.1"], "config_flow": true, "codeowners": ["@Cereal2nd", "@brefra"], "dependencies": ["usb"], diff --git a/requirements_all.txt b/requirements_all.txt index a3c604dfc2e..ada6fa28325 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2449,7 +2449,7 @@ vallox-websocket-api==2.12.0 vehicle==0.4.0 # homeassistant.components.velbus -velbus-aio==2022.6.2 +velbus-aio==2022.9.1 # homeassistant.components.venstar venstarcolortouch==0.18 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index d974a7a181f..9847410eafc 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1677,7 +1677,7 @@ vallox-websocket-api==2.12.0 vehicle==0.4.0 # homeassistant.components.velbus -velbus-aio==2022.6.2 +velbus-aio==2022.9.1 # homeassistant.components.venstar venstarcolortouch==0.18 From 3b025b211e64735b9511d8f86f963216dd774407 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Thu, 8 Sep 2022 20:15:27 +0200 Subject: [PATCH 08/15] Fix zwave_js device re-interview (#78046) * Handle stale node and entity info on re-interview * Add test * Unsubscribe on config entry unload --- homeassistant/components/zwave_js/__init__.py | 34 ++++++++--- homeassistant/components/zwave_js/entity.py | 13 ++++- homeassistant/components/zwave_js/update.py | 8 +++ tests/components/zwave_js/test_init.py | 57 +++++++++++++++++++ 4 files changed, 103 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/zwave_js/__init__.py b/homeassistant/components/zwave_js/__init__.py index 98219520693..066bc5101ae 100644 --- a/homeassistant/components/zwave_js/__init__.py +++ b/homeassistant/components/zwave_js/__init__.py @@ -313,19 +313,24 @@ class ControllerEvents: node, ) + LOGGER.debug("Node added: %s", node.node_id) + + # Listen for ready node events, both new and re-interview. + self.config_entry.async_on_unload( + node.on( + "ready", + lambda event: self.hass.async_create_task( + self.node_events.async_on_node_ready(event["node"]) + ), + ) + ) + # we only want to run discovery when the node has reached ready state, # otherwise we'll have all kinds of missing info issues. if node.ready: await self.node_events.async_on_node_ready(node) return - # if node is not yet ready, register one-time callback for ready state - LOGGER.debug("Node added: %s - waiting for it to become ready", node.node_id) - node.once( - "ready", - lambda event: self.hass.async_create_task( - self.node_events.async_on_node_ready(event["node"]) - ), - ) + # we do submit the node to device registry so user has # some visual feedback that something is (in the process of) being added self.register_node_in_dev_reg(node) @@ -414,12 +419,25 @@ class NodeEvents: async def async_on_node_ready(self, node: ZwaveNode) -> None: """Handle node ready event.""" LOGGER.debug("Processing node %s", node) + driver = self.controller_events.driver_events.driver # register (or update) node in device registry device = self.controller_events.register_node_in_dev_reg(node) # We only want to create the defaultdict once, even on reinterviews if device.id not in self.controller_events.registered_unique_ids: self.controller_events.registered_unique_ids[device.id] = defaultdict(set) + # Remove any old value ids if this is a reinterview. + self.controller_events.discovered_value_ids.pop(device.id, None) + # Remove stale entities that may exist from a previous interview. + async_dispatcher_send( + self.hass, + ( + f"{DOMAIN}_" + f"{get_valueless_base_unique_id(driver, node)}_" + "remove_entity_on_ready_node" + ), + ) + value_updates_disc_info: dict[str, ZwaveDiscoveryInfo] = {} # run discovery on all node values and create/update entities diff --git a/homeassistant/components/zwave_js/entity.py b/homeassistant/components/zwave_js/entity.py index 79dd1d27a4c..65f00b5022a 100644 --- a/homeassistant/components/zwave_js/entity.py +++ b/homeassistant/components/zwave_js/entity.py @@ -12,7 +12,7 @@ from homeassistant.helpers.entity import DeviceInfo, Entity from .const import DOMAIN, LOGGER from .discovery import ZwaveDiscoveryInfo -from .helpers import get_device_id, get_unique_id +from .helpers import get_device_id, get_unique_id, get_valueless_base_unique_id EVENT_VALUE_UPDATED = "value updated" EVENT_VALUE_REMOVED = "value removed" @@ -96,6 +96,17 @@ class ZWaveBaseEntity(Entity): self.async_on_remove( self.info.node.on(EVENT_VALUE_REMOVED, self._value_removed) ) + self.async_on_remove( + async_dispatcher_connect( + self.hass, + ( + f"{DOMAIN}_" + f"{get_valueless_base_unique_id(self.driver, self.info.node)}_" + "remove_entity_on_ready_node" + ), + self.async_remove, + ) + ) for status_event in (EVENT_ALIVE, EVENT_DEAD): self.async_on_remove( diff --git a/homeassistant/components/zwave_js/update.py b/homeassistant/components/zwave_js/update.py index f5604c8280c..4f25d138aea 100644 --- a/homeassistant/components/zwave_js/update.py +++ b/homeassistant/components/zwave_js/update.py @@ -235,6 +235,14 @@ class ZWaveNodeFirmwareUpdate(UpdateEntity): ) ) + self.async_on_remove( + async_dispatcher_connect( + self.hass, + f"{DOMAIN}_{self._base_unique_id}_remove_entity_on_ready_node", + self.async_remove, + ) + ) + self.async_on_remove(async_at_start(self.hass, self._async_update)) async def async_will_remove_from_hass(self) -> None: diff --git a/tests/components/zwave_js/test_init.py b/tests/components/zwave_js/test_init.py index d038949d494..63cbc090e7d 100644 --- a/tests/components/zwave_js/test_init.py +++ b/tests/components/zwave_js/test_init.py @@ -3,6 +3,7 @@ from copy import deepcopy from unittest.mock import call, patch import pytest +from zwave_js_server.client import Client from zwave_js_server.event import Event from zwave_js_server.exceptions import BaseZwaveJSServerError, InvalidServerVersion from zwave_js_server.model.node import Node @@ -12,6 +13,7 @@ from homeassistant.components.zwave_js.const import DOMAIN from homeassistant.components.zwave_js.helpers import get_device_id from homeassistant.config_entries import ConfigEntryDisabler, ConfigEntryState from homeassistant.const import STATE_UNAVAILABLE +from homeassistant.core import HomeAssistant from homeassistant.helpers import ( area_registry as ar, device_registry as dr, @@ -242,6 +244,61 @@ async def test_existing_node_ready(hass, client, multisensor_6, integration): ) +async def test_existing_node_reinterview( + hass: HomeAssistant, + client: Client, + multisensor_6_state: dict, + multisensor_6: Node, + integration: MockConfigEntry, +) -> None: + """Test we handle a node re-interview firing a node ready event.""" + dev_reg = dr.async_get(hass) + node = multisensor_6 + assert client.driver is not None + air_temperature_device_id = f"{client.driver.controller.home_id}-{node.node_id}" + air_temperature_device_id_ext = ( + f"{air_temperature_device_id}-{node.manufacturer_id}:" + f"{node.product_type}:{node.product_id}" + ) + + state = hass.states.get(AIR_TEMPERATURE_SENSOR) + + assert state # entity and device added + assert state.state != STATE_UNAVAILABLE + + device = dev_reg.async_get_device(identifiers={(DOMAIN, air_temperature_device_id)}) + assert device + assert device == dev_reg.async_get_device( + identifiers={(DOMAIN, air_temperature_device_id_ext)} + ) + assert device.sw_version == "1.12" + + node_state = deepcopy(multisensor_6_state) + node_state["firmwareVersion"] = "1.13" + event = Event( + type="ready", + data={ + "source": "node", + "event": "ready", + "nodeId": node.node_id, + "nodeState": node_state, + }, + ) + client.driver.receive_event(event) + await hass.async_block_till_done() + + state = hass.states.get(AIR_TEMPERATURE_SENSOR) + + assert state + assert state.state != STATE_UNAVAILABLE + device = dev_reg.async_get_device(identifiers={(DOMAIN, air_temperature_device_id)}) + assert device + assert device == dev_reg.async_get_device( + identifiers={(DOMAIN, air_temperature_device_id_ext)} + ) + assert device.sw_version == "1.13" + + async def test_existing_node_not_ready(hass, zp3111_not_ready, client, integration): """Test we handle a non-ready node that exists during integration setup.""" dev_reg = dr.async_get(hass) From 2ddd1b516caadcce224e76cdfd173bd213883363 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 8 Sep 2022 08:58:53 -0500 Subject: [PATCH 09/15] Bump bluetooth-adapters to 0.3.5 (#78052) --- 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 ca6a76c55ae..aa890a47a41 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.4", + "bluetooth-adapters==0.3.5", "bluetooth-auto-recovery==0.3.1" ], "codeowners": ["@bdraco"], diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 7c11725e460..9608cdbfe9e 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.4 +bluetooth-adapters==0.3.5 bluetooth-auto-recovery==0.3.1 certifi>=2021.5.30 ciso8601==2.2.0 diff --git a/requirements_all.txt b/requirements_all.txt index ada6fa28325..9c59434c26f 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.4 +bluetooth-adapters==0.3.5 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.3.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9847410eafc..69b816f2af7 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.4 +bluetooth-adapters==0.3.5 # homeassistant.components.bluetooth bluetooth-auto-recovery==0.3.1 From cfe8ebdad4bd73ac70da796c72a0119fa80f3b5a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 8 Sep 2022 12:15:26 -0500 Subject: [PATCH 10/15] Bump bluetooth-auto-recovery to 0.3.2 (#78063) --- 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 aa890a47a41..3043d6412a4 100644 --- a/homeassistant/components/bluetooth/manifest.json +++ b/homeassistant/components/bluetooth/manifest.json @@ -7,7 +7,7 @@ "requirements": [ "bleak==0.16.0", "bluetooth-adapters==0.3.5", - "bluetooth-auto-recovery==0.3.1" + "bluetooth-auto-recovery==0.3.2" ], "codeowners": ["@bdraco"], "config_flow": true, diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 9608cdbfe9e..1a1fcb9da84 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -12,7 +12,7 @@ awesomeversion==22.8.0 bcrypt==3.1.7 bleak==0.16.0 bluetooth-adapters==0.3.5 -bluetooth-auto-recovery==0.3.1 +bluetooth-auto-recovery==0.3.2 certifi>=2021.5.30 ciso8601==2.2.0 cryptography==37.0.4 diff --git a/requirements_all.txt b/requirements_all.txt index 9c59434c26f..34645632890 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -433,7 +433,7 @@ bluemaestro-ble==0.2.0 bluetooth-adapters==0.3.5 # homeassistant.components.bluetooth -bluetooth-auto-recovery==0.3.1 +bluetooth-auto-recovery==0.3.2 # homeassistant.components.bond bond-async==0.1.22 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 69b816f2af7..f3927d4ed8b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -344,7 +344,7 @@ bluemaestro-ble==0.2.0 bluetooth-adapters==0.3.5 # homeassistant.components.bluetooth -bluetooth-auto-recovery==0.3.1 +bluetooth-auto-recovery==0.3.2 # homeassistant.components.bond bond-async==0.1.22 From 760853f6155586e7d69347feff3552a406f3ee29 Mon Sep 17 00:00:00 2001 From: Aaron Bach Date: Thu, 8 Sep 2022 14:41:09 -0600 Subject: [PATCH 11/15] Fix bug with 1st gen RainMachine controllers and unknown API calls (#78070) Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> --- homeassistant/components/rainmachine/__init__.py | 12 ++++++++++-- .../components/rainmachine/binary_sensor.py | 4 +++- homeassistant/components/rainmachine/manifest.json | 2 +- homeassistant/components/rainmachine/sensor.py | 12 +++++++----- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 6 files changed, 23 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/rainmachine/__init__.py b/homeassistant/components/rainmachine/__init__.py index 52de2e1c61a..756dc9b958d 100644 --- a/homeassistant/components/rainmachine/__init__.py +++ b/homeassistant/components/rainmachine/__init__.py @@ -9,7 +9,7 @@ from typing import Any from regenmaschine import Client from regenmaschine.controller import Controller -from regenmaschine.errors import RainMachineError +from regenmaschine.errors import RainMachineError, UnknownAPICallError import voluptuous as vol from homeassistant.config_entries import ConfigEntry, ConfigEntryState @@ -190,7 +190,9 @@ async def async_update_programs_and_zones( ) -async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: +async def async_setup_entry( # noqa: C901 + hass: HomeAssistant, entry: ConfigEntry +) -> bool: """Set up RainMachine as config entry.""" websession = aiohttp_client.async_get_clientsession(hass) client = Client(session=websession) @@ -244,6 +246,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: data = await controller.restrictions.universal() else: data = await controller.zones.all(details=True, include_inactive=True) + except UnknownAPICallError: + LOGGER.info( + "Skipping unsupported API call for controller %s: %s", + controller.name, + api_category, + ) except RainMachineError as err: raise UpdateFailed(err) from err diff --git a/homeassistant/components/rainmachine/binary_sensor.py b/homeassistant/components/rainmachine/binary_sensor.py index 3db64240788..48f11f598c9 100644 --- a/homeassistant/components/rainmachine/binary_sensor.py +++ b/homeassistant/components/rainmachine/binary_sensor.py @@ -175,7 +175,9 @@ class ProvisionSettingsBinarySensor(RainMachineEntity, BinarySensorEntity): def update_from_latest_data(self) -> None: """Update the state.""" if self.entity_description.key == TYPE_FLOW_SENSOR: - self._attr_is_on = self.coordinator.data["system"].get("useFlowSensor") + self._attr_is_on = self.coordinator.data.get("system", {}).get( + "useFlowSensor" + ) class UniversalRestrictionsBinarySensor(RainMachineEntity, BinarySensorEntity): diff --git a/homeassistant/components/rainmachine/manifest.json b/homeassistant/components/rainmachine/manifest.json index b183fc1b24f..a6cc86e5055 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.08.0"], + "requirements": ["regenmaschine==2022.09.0"], "codeowners": ["@bachya"], "iot_class": "local_polling", "homekit": { diff --git a/homeassistant/components/rainmachine/sensor.py b/homeassistant/components/rainmachine/sensor.py index e2e602b945b..32364e08199 100644 --- a/homeassistant/components/rainmachine/sensor.py +++ b/homeassistant/components/rainmachine/sensor.py @@ -273,12 +273,14 @@ class ProvisionSettingsSensor(RainMachineEntity, SensorEntity): def update_from_latest_data(self) -> None: """Update the state.""" if self.entity_description.key == TYPE_FLOW_SENSOR_CLICK_M3: - self._attr_native_value = self.coordinator.data["system"].get( + self._attr_native_value = self.coordinator.data.get("system", {}).get( "flowSensorClicksPerCubicMeter" ) elif self.entity_description.key == TYPE_FLOW_SENSOR_CONSUMED_LITERS: - clicks = self.coordinator.data["system"].get("flowSensorWateringClicks") - clicks_per_m3 = self.coordinator.data["system"].get( + clicks = self.coordinator.data.get("system", {}).get( + "flowSensorWateringClicks" + ) + clicks_per_m3 = self.coordinator.data.get("system", {}).get( "flowSensorClicksPerCubicMeter" ) @@ -287,11 +289,11 @@ class ProvisionSettingsSensor(RainMachineEntity, SensorEntity): else: self._attr_native_value = None elif self.entity_description.key == TYPE_FLOW_SENSOR_START_INDEX: - self._attr_native_value = self.coordinator.data["system"].get( + self._attr_native_value = self.coordinator.data.get("system", {}).get( "flowSensorStartIndex" ) elif self.entity_description.key == TYPE_FLOW_SENSOR_WATERING_CLICKS: - self._attr_native_value = self.coordinator.data["system"].get( + self._attr_native_value = self.coordinator.data.get("system", {}).get( "flowSensorWateringClicks" ) diff --git a/requirements_all.txt b/requirements_all.txt index 34645632890..16df31128aa 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.08.0 +regenmaschine==2022.09.0 # homeassistant.components.renault renault-api==0.1.11 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f3927d4ed8b..1f94d4041a7 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.08.0 +regenmaschine==2022.09.0 # homeassistant.components.renault renault-api==0.1.11 From d559b6482ac297c493b6294222e2e80ab27726ae Mon Sep 17 00:00:00 2001 From: Nathan Spencer Date: Thu, 8 Sep 2022 11:13:20 -0600 Subject: [PATCH 12/15] Bump pylitterbot to 2022.9.1 (#78071) --- homeassistant/components/litterrobot/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/litterrobot/conftest.py | 8 +++++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/litterrobot/manifest.json b/homeassistant/components/litterrobot/manifest.json index f25b4525877..a4c9f3cd54e 100644 --- a/homeassistant/components/litterrobot/manifest.json +++ b/homeassistant/components/litterrobot/manifest.json @@ -3,7 +3,7 @@ "name": "Litter-Robot", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/litterrobot", - "requirements": ["pylitterbot==2022.8.2"], + "requirements": ["pylitterbot==2022.9.1"], "codeowners": ["@natekspencer", "@tkdrob"], "dhcp": [{ "hostname": "litter-robot4" }], "iot_class": "cloud_polling", diff --git a/requirements_all.txt b/requirements_all.txt index 16df31128aa..e740b8d0017 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1668,7 +1668,7 @@ pylibrespot-java==0.1.0 pylitejet==0.3.0 # homeassistant.components.litterrobot -pylitterbot==2022.8.2 +pylitterbot==2022.9.1 # homeassistant.components.lutron_caseta pylutron-caseta==0.13.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1f94d4041a7..493ff7c4e5b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1166,7 +1166,7 @@ pylibrespot-java==0.1.0 pylitejet==0.3.0 # homeassistant.components.litterrobot -pylitterbot==2022.8.2 +pylitterbot==2022.9.1 # homeassistant.components.lutron_caseta pylutron-caseta==0.13.1 diff --git a/tests/components/litterrobot/conftest.py b/tests/components/litterrobot/conftest.py index e5d5e730b61..34132ec66d6 100644 --- a/tests/components/litterrobot/conftest.py +++ b/tests/components/litterrobot/conftest.py @@ -17,13 +17,13 @@ from tests.common import MockConfigEntry def create_mock_robot( - robot_data: dict | None = None, side_effect: Any | None = None + robot_data: dict | None, account: Account, side_effect: Any | None = None ) -> Robot: """Create a mock Litter-Robot device.""" if not robot_data: robot_data = {} - robot = LitterRobot3(data={**ROBOT_DATA, **robot_data}) + robot = LitterRobot3(data={**ROBOT_DATA, **robot_data}, account=account) robot.start_cleaning = AsyncMock(side_effect=side_effect) robot.set_power_status = AsyncMock(side_effect=side_effect) robot.reset_waste_drawer = AsyncMock(side_effect=side_effect) @@ -44,7 +44,9 @@ def create_mock_account( account = MagicMock(spec=Account) account.connect = AsyncMock() account.refresh_robots = AsyncMock() - account.robots = [] if skip_robots else [create_mock_robot(robot_data, side_effect)] + account.robots = ( + [] if skip_robots else [create_mock_robot(robot_data, account, side_effect)] + ) return account From c873eae79ce621391f6f8890c40477882fd39f61 Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Thu, 8 Sep 2022 22:49:49 +0200 Subject: [PATCH 13/15] Allow OpenWeatherMap config flow to test using old API to pass (#78074) Co-authored-by: Paulus Schoutsen --- homeassistant/components/openweathermap/config_flow.py | 2 +- tests/components/openweathermap/test_config_flow.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/openweathermap/config_flow.py b/homeassistant/components/openweathermap/config_flow.py index 612965bdb2f..c418231946f 100644 --- a/homeassistant/components/openweathermap/config_flow.py +++ b/homeassistant/components/openweathermap/config_flow.py @@ -130,4 +130,4 @@ class OpenWeatherMapOptionsFlow(config_entries.OptionsFlow): async def _is_owm_api_online(hass, api_key, lat, lon): owm = OWM(api_key).weather_manager() - return await hass.async_add_executor_job(owm.one_call, lat, lon) + return await hass.async_add_executor_job(owm.weather_at_coords, lat, lon) diff --git a/tests/components/openweathermap/test_config_flow.py b/tests/components/openweathermap/test_config_flow.py index 12ee849d3d2..40931dc2ce2 100644 --- a/tests/components/openweathermap/test_config_flow.py +++ b/tests/components/openweathermap/test_config_flow.py @@ -208,6 +208,8 @@ def _create_mocked_owm(is_api_online: bool): mocked_owm.one_call.return_value = one_call - mocked_owm.weather_manager.return_value.one_call.return_value = is_api_online + mocked_owm.weather_manager.return_value.weather_at_coords.return_value = ( + is_api_online + ) return mocked_owm From a4f398a7509a2afb3fb89f5e2082ca7ad0800a7a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 8 Sep 2022 16:50:47 -0400 Subject: [PATCH 14/15] Bumped version to 2022.9.1 --- 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 c3a59a88aae..38a50aa8fc3 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 = "0" +PATCH_VERSION: Final = "1" __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 9b05ae89191..10bab1ac2f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2022.9.0" +version = "2022.9.1" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst" From fee9a303ffdb8cff6fc8c9f8d606040d81f45341 Mon Sep 17 00:00:00 2001 From: rlippmann <70883373+rlippmann@users.noreply.github.com> Date: Thu, 8 Sep 2022 21:01:43 -0400 Subject: [PATCH 15/15] Fix issue #77920 - ecobee remote sensors not updating (#78035) --- homeassistant/components/ecobee/sensor.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/ecobee/sensor.py b/homeassistant/components/ecobee/sensor.py index a7d5639ae2c..9d8793efc29 100644 --- a/homeassistant/components/ecobee/sensor.py +++ b/homeassistant/components/ecobee/sensor.py @@ -29,7 +29,7 @@ from .const import DOMAIN, ECOBEE_MODEL_TO_NAME, MANUFACTURER class EcobeeSensorEntityDescriptionMixin: """Represent the required ecobee entity description attributes.""" - runtime_key: str + runtime_key: str | None @dataclass @@ -46,7 +46,7 @@ SENSOR_TYPES: tuple[EcobeeSensorEntityDescription, ...] = ( native_unit_of_measurement=TEMP_FAHRENHEIT, device_class=SensorDeviceClass.TEMPERATURE, state_class=SensorStateClass.MEASUREMENT, - runtime_key="actualTemperature", + runtime_key=None, ), EcobeeSensorEntityDescription( key="humidity", @@ -54,7 +54,7 @@ SENSOR_TYPES: tuple[EcobeeSensorEntityDescription, ...] = ( native_unit_of_measurement=PERCENTAGE, device_class=SensorDeviceClass.HUMIDITY, state_class=SensorStateClass.MEASUREMENT, - runtime_key="actualHumidity", + runtime_key=None, ), EcobeeSensorEntityDescription( key="co2PPM", @@ -194,6 +194,11 @@ class EcobeeSensor(SensorEntity): for item in sensor["capability"]: if item["type"] != self.entity_description.key: continue - thermostat = self.data.ecobee.get_thermostat(self.index) - self._state = thermostat["runtime"][self.entity_description.runtime_key] + if self.entity_description.runtime_key is None: + self._state = item["value"] + else: + thermostat = self.data.ecobee.get_thermostat(self.index) + self._state = thermostat["runtime"][ + self.entity_description.runtime_key + ] break