From 10c7725a901bc4328979e8aa0454e4bfa5832ce4 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Sat, 12 Feb 2022 22:53:15 +0100 Subject: [PATCH 01/19] Fix Spotify session token refresh (#66390) --- homeassistant/components/spotify/__init__.py | 6 ++++++ homeassistant/components/spotify/media_player.py | 10 ++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/spotify/__init__.py b/homeassistant/components/spotify/__init__.py index c3e9504c05c..446c2ace82c 100644 --- a/homeassistant/components/spotify/__init__.py +++ b/homeassistant/components/spotify/__init__.py @@ -121,6 +121,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: raise ConfigEntryNotReady from err async def _update_devices() -> list[dict[str, Any]]: + if not session.valid_token: + await session.async_ensure_token_valid() + await hass.async_add_executor_job( + spotify.set_auth, session.token["access_token"] + ) + try: devices: dict[str, Any] | None = await hass.async_add_executor_job( spotify.devices diff --git a/homeassistant/components/spotify/media_player.py b/homeassistant/components/spotify/media_player.py index b3bb2efd1c0..06efb5558d1 100644 --- a/homeassistant/components/spotify/media_player.py +++ b/homeassistant/components/spotify/media_player.py @@ -515,9 +515,7 @@ class SpotifyMediaPlayer(MediaPlayerEntity): run_coroutine_threadsafe( self._session.async_ensure_token_valid(), self.hass.loop ).result() - self._spotify_data[DATA_SPOTIFY_CLIENT] = Spotify( - auth=self._session.token["access_token"] - ) + self._spotify.set_auth(auth=self._session.token["access_token"]) current = self._spotify.current_playback() self._currently_playing = current or {} @@ -581,7 +579,11 @@ async def async_browse_media_internal( partial(library_payload, can_play_artist=can_play_artist) ) - await session.async_ensure_token_valid() + if not session.valid_token: + await session.async_ensure_token_valid() + await hass.async_add_executor_job( + spotify.set_auth, session.token["access_token"] + ) # Strip prefix media_content_type = media_content_type[len(MEDIA_PLAYER_PREFIX) :] From 23a68f5d494f84a5a8536d0049a7a1bf6870021a Mon Sep 17 00:00:00 2001 From: uSlackr Date: Mon, 14 Feb 2022 12:17:19 -0500 Subject: [PATCH 02/19] Correct modbus address limits (#66367) --- homeassistant/components/modbus/services.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/modbus/services.yaml b/homeassistant/components/modbus/services.yaml index 835927e4627..87e8b98fa21 100644 --- a/homeassistant/components/modbus/services.yaml +++ b/homeassistant/components/modbus/services.yaml @@ -8,8 +8,8 @@ write_coil: required: true selector: number: - min: 1 - max: 255 + min: 0 + max: 65535 state: name: State description: State to write. @@ -42,8 +42,8 @@ write_register: required: true selector: number: - min: 1 - max: 255 + min: 0 + max: 65535 unit: name: Unit description: Address of the modbus unit. From 51dd3c88e99d3b2b214fd7e6b4f8bec008370bc8 Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Sat, 12 Feb 2022 22:58:35 +0100 Subject: [PATCH 03/19] Fix mesh role for Fritz old devices (#66369) --- homeassistant/components/fritz/common.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/fritz/common.py b/homeassistant/components/fritz/common.py index ae506989e00..b005e536ed6 100644 --- a/homeassistant/components/fritz/common.py +++ b/homeassistant/components/fritz/common.py @@ -327,11 +327,19 @@ class FritzBoxTools(update_coordinator.DataUpdateCoordinator): _LOGGER.debug("Checking host info for FRITZ!Box device %s", self.host) self._update_available, self._latest_firmware = self._update_device_info() - try: - topology = self.fritz_hosts.get_mesh_topology() - except FritzActionError: - self.mesh_role = MeshRoles.SLAVE - return + if ( + "Hosts1" not in self.connection.services + or "X_AVM-DE_GetMeshListPath" + not in self.connection.services["Hosts1"].actions + ): + self.mesh_role = MeshRoles.NONE + else: + try: + topology = self.fritz_hosts.get_mesh_topology() + except FritzActionError: + self.mesh_role = MeshRoles.SLAVE + # Avoid duplicating device trackers + return _LOGGER.debug("Checking devices for FRITZ!Box device %s", self.host) _default_consider_home = DEFAULT_CONSIDER_HOME.total_seconds() From d315890c6135b14cb413104bbc87225093082222 Mon Sep 17 00:00:00 2001 From: Dave T <17680170+davet2001@users.noreply.github.com> Date: Sat, 12 Feb 2022 14:22:21 +0000 Subject: [PATCH 04/19] Fix missing refactors of EntityCategory.XXX (#66379) * Fix missing refactors of EntityCategory.XXX * Fix entity_category refactor for homewizard --- homeassistant/components/fritz/button.py | 11 +++++------ homeassistant/components/fritzbox/binary_sensor.py | 6 +++--- homeassistant/components/goodwe/number.py | 8 ++++---- homeassistant/components/goodwe/select.py | 5 ++--- homeassistant/components/homewizard/sensor.py | 11 +++++------ homeassistant/components/onvif/button.py | 4 ++-- homeassistant/components/vicare/button.py | 4 ++-- homeassistant/components/zha/button.py | 5 +++-- homeassistant/components/zha/select.py | 5 +++-- homeassistant/components/zha/sensor.py | 3 +-- 10 files changed, 30 insertions(+), 32 deletions(-) diff --git a/homeassistant/components/fritz/button.py b/homeassistant/components/fritz/button.py index d17d1f4d9ef..e72af839e4c 100644 --- a/homeassistant/components/fritz/button.py +++ b/homeassistant/components/fritz/button.py @@ -12,10 +12,9 @@ from homeassistant.components.button import ( ButtonEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ENTITY_CATEGORY_CONFIG from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC -from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity import DeviceInfo, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from .common import AvmWrapper @@ -41,28 +40,28 @@ BUTTONS: Final = [ key="firmware_update", name="Firmware Update", device_class=ButtonDeviceClass.UPDATE, - entity_category=ENTITY_CATEGORY_CONFIG, + entity_category=EntityCategory.CONFIG, press_action=lambda avm_wrapper: avm_wrapper.async_trigger_firmware_update(), ), FritzButtonDescription( key="reboot", name="Reboot", device_class=ButtonDeviceClass.RESTART, - entity_category=ENTITY_CATEGORY_CONFIG, + entity_category=EntityCategory.CONFIG, press_action=lambda avm_wrapper: avm_wrapper.async_trigger_reboot(), ), FritzButtonDescription( key="reconnect", name="Reconnect", device_class=ButtonDeviceClass.RESTART, - entity_category=ENTITY_CATEGORY_CONFIG, + entity_category=EntityCategory.CONFIG, press_action=lambda avm_wrapper: avm_wrapper.async_trigger_reconnect(), ), FritzButtonDescription( key="cleanup", name="Cleanup", icon="mdi:broom", - entity_category=ENTITY_CATEGORY_CONFIG, + entity_category=EntityCategory.CONFIG, press_action=lambda avm_wrapper: avm_wrapper.async_trigger_cleanup(), ), ] diff --git a/homeassistant/components/fritzbox/binary_sensor.py b/homeassistant/components/fritzbox/binary_sensor.py index b58f3311cb5..b80d853e562 100644 --- a/homeassistant/components/fritzbox/binary_sensor.py +++ b/homeassistant/components/fritzbox/binary_sensor.py @@ -13,8 +13,8 @@ from homeassistant.components.binary_sensor import ( BinarySensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ENTITY_CATEGORY_CONFIG from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import FritzBoxEntity @@ -49,7 +49,7 @@ BINARY_SENSOR_TYPES: Final[tuple[FritzBinarySensorEntityDescription, ...]] = ( key="lock", name="Button Lock on Device", device_class=BinarySensorDeviceClass.LOCK, - entity_category=ENTITY_CATEGORY_CONFIG, + entity_category=EntityCategory.CONFIG, suitable=lambda device: device.lock is not None, is_on=lambda device: not device.lock, ), @@ -57,7 +57,7 @@ BINARY_SENSOR_TYPES: Final[tuple[FritzBinarySensorEntityDescription, ...]] = ( key="device_lock", name="Button Lock via UI", device_class=BinarySensorDeviceClass.LOCK, - entity_category=ENTITY_CATEGORY_CONFIG, + entity_category=EntityCategory.CONFIG, suitable=lambda device: device.device_lock is not None, is_on=lambda device: not device.device_lock, ), diff --git a/homeassistant/components/goodwe/number.py b/homeassistant/components/goodwe/number.py index 06a31a4e10a..80c7885f26c 100644 --- a/homeassistant/components/goodwe/number.py +++ b/homeassistant/components/goodwe/number.py @@ -9,9 +9,9 @@ from goodwe import Inverter, InverterError from homeassistant.components.number import NumberEntity, NumberEntityDescription from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ENTITY_CATEGORY_CONFIG, PERCENTAGE, POWER_WATT +from homeassistant.const import PERCENTAGE, POWER_WATT from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity import DeviceInfo, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN, KEY_DEVICE_INFO, KEY_INVERTER @@ -39,7 +39,7 @@ NUMBERS = ( key="grid_export_limit", name="Grid export limit", icon="mdi:transmission-tower", - entity_category=ENTITY_CATEGORY_CONFIG, + entity_category=EntityCategory.CONFIG, unit_of_measurement=POWER_WATT, getter=lambda inv: inv.get_grid_export_limit(), setter=lambda inv, val: inv.set_grid_export_limit(val), @@ -51,7 +51,7 @@ NUMBERS = ( key="battery_discharge_depth", name="Depth of discharge (on-grid)", icon="mdi:battery-arrow-down", - entity_category=ENTITY_CATEGORY_CONFIG, + entity_category=EntityCategory.CONFIG, unit_of_measurement=PERCENTAGE, getter=lambda inv: inv.get_ongrid_battery_dod(), setter=lambda inv, val: inv.set_ongrid_battery_dod(val), diff --git a/homeassistant/components/goodwe/select.py b/homeassistant/components/goodwe/select.py index 985c799110d..c8fa44b7e26 100644 --- a/homeassistant/components/goodwe/select.py +++ b/homeassistant/components/goodwe/select.py @@ -5,9 +5,8 @@ from goodwe import Inverter, InverterError from homeassistant.components.select import SelectEntity, SelectEntityDescription from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ENTITY_CATEGORY_CONFIG from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity import DeviceInfo, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN, KEY_DEVICE_INFO, KEY_INVERTER @@ -26,7 +25,7 @@ OPERATION_MODE = SelectEntityDescription( key="operation_mode", name="Inverter operation mode", icon="mdi:solar-power", - entity_category=ENTITY_CATEGORY_CONFIG, + entity_category=EntityCategory.CONFIG, ) diff --git a/homeassistant/components/homewizard/sensor.py b/homeassistant/components/homewizard/sensor.py index c2a11386cf4..e9a09f9db86 100644 --- a/homeassistant/components/homewizard/sensor.py +++ b/homeassistant/components/homewizard/sensor.py @@ -16,13 +16,12 @@ from homeassistant.const import ( DEVICE_CLASS_GAS, DEVICE_CLASS_POWER, ENERGY_KILO_WATT_HOUR, - ENTITY_CATEGORY_DIAGNOSTIC, PERCENTAGE, POWER_WATT, VOLUME_CUBIC_METERS, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity import DeviceInfo, EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -37,19 +36,19 @@ SENSORS: Final[tuple[SensorEntityDescription, ...]] = ( key="smr_version", name="DSMR Version", icon="mdi:counter", - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="meter_model", name="Smart Meter Model", icon="mdi:gauge", - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="wifi_ssid", name="Wifi SSID", icon="mdi:wifi", - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + entity_category=EntityCategory.DIAGNOSTIC, ), SensorEntityDescription( key="wifi_strength", @@ -57,7 +56,7 @@ SENSORS: Final[tuple[SensorEntityDescription, ...]] = ( icon="mdi:wifi", native_unit_of_measurement=PERCENTAGE, state_class=STATE_CLASS_MEASUREMENT, - entity_category=ENTITY_CATEGORY_DIAGNOSTIC, + entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, ), SensorEntityDescription( diff --git a/homeassistant/components/onvif/button.py b/homeassistant/components/onvif/button.py index 034573299e6..23ea5124e61 100644 --- a/homeassistant/components/onvif/button.py +++ b/homeassistant/components/onvif/button.py @@ -2,8 +2,8 @@ from homeassistant.components.button import ButtonDeviceClass, ButtonEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ENTITY_CATEGORY_CONFIG from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from .base import ONVIFBaseEntity @@ -25,7 +25,7 @@ class RebootButton(ONVIFBaseEntity, ButtonEntity): """Defines a ONVIF reboot button.""" _attr_device_class = ButtonDeviceClass.RESTART - _attr_entity_category = ENTITY_CATEGORY_CONFIG + _attr_entity_category = EntityCategory.CONFIG def __init__(self, device: ONVIFDevice) -> None: """Initialize the button entity.""" diff --git a/homeassistant/components/vicare/button.py b/homeassistant/components/vicare/button.py index 35133b55bd1..e924a735e0f 100644 --- a/homeassistant/components/vicare/button.py +++ b/homeassistant/components/vicare/button.py @@ -14,8 +14,8 @@ import requests from homeassistant.components.button import ButtonEntity, ButtonEntityDescription from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ENTITY_CATEGORY_CONFIG from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from . import ViCareRequiredKeysMixin @@ -36,7 +36,7 @@ BUTTON_DESCRIPTIONS: tuple[ViCareButtonEntityDescription, ...] = ( key=BUTTON_DHW_ACTIVATE_ONETIME_CHARGE, name="Activate one-time charge", icon="mdi:shower-head", - entity_category=ENTITY_CATEGORY_CONFIG, + entity_category=EntityCategory.CONFIG, value_getter=lambda api: api.activateOneTimeCharge(), ), ) diff --git a/homeassistant/components/zha/button.py b/homeassistant/components/zha/button.py index 90148ba42f3..abfa94f5906 100644 --- a/homeassistant/components/zha/button.py +++ b/homeassistant/components/zha/button.py @@ -8,9 +8,10 @@ from typing import Any from homeassistant.components.button import ButtonDeviceClass, ButtonEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ENTITY_CATEGORY_DIAGNOSTIC, Platform +from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from .core import discovery @@ -96,7 +97,7 @@ class ZHAIdentifyButton(ZHAButton): return cls(unique_id, zha_device, channels, **kwargs) _attr_device_class: ButtonDeviceClass = ButtonDeviceClass.UPDATE - _attr_entity_category = ENTITY_CATEGORY_DIAGNOSTIC + _attr_entity_category = EntityCategory.DIAGNOSTIC _command_name = "identify" def get_args(self) -> list[Any]: diff --git a/homeassistant/components/zha/select.py b/homeassistant/components/zha/select.py index ff76023d96d..7cb214566d1 100644 --- a/homeassistant/components/zha/select.py +++ b/homeassistant/components/zha/select.py @@ -8,9 +8,10 @@ from zigpy.zcl.clusters.security import IasWd from homeassistant.components.select import SelectEntity from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ENTITY_CATEGORY_CONFIG, STATE_UNKNOWN, Platform +from homeassistant.const import STATE_UNKNOWN, Platform from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback from .core import discovery @@ -46,7 +47,7 @@ async def async_setup_entry( class ZHAEnumSelectEntity(ZhaEntity, SelectEntity): """Representation of a ZHA select entity.""" - _attr_entity_category = ENTITY_CATEGORY_CONFIG + _attr_entity_category = EntityCategory.CONFIG _enum: Enum = None def __init__( diff --git a/homeassistant/components/zha/sensor.py b/homeassistant/components/zha/sensor.py index eb79634695f..1e7d4f28a38 100644 --- a/homeassistant/components/zha/sensor.py +++ b/homeassistant/components/zha/sensor.py @@ -25,7 +25,6 @@ from homeassistant.const import ( ELECTRIC_CURRENT_AMPERE, ELECTRIC_POTENTIAL_VOLT, ENERGY_KILO_WATT_HOUR, - ENTITY_CATEGORY_DIAGNOSTIC, LIGHT_LUX, PERCENTAGE, POWER_VOLT_AMPERE, @@ -699,7 +698,7 @@ class RSSISensor(Sensor, id_suffix="rssi"): _state_class: SensorStateClass = SensorStateClass.MEASUREMENT _device_class: SensorDeviceClass = SensorDeviceClass.SIGNAL_STRENGTH - _attr_entity_category = ENTITY_CATEGORY_DIAGNOSTIC + _attr_entity_category = EntityCategory.DIAGNOSTIC _attr_entity_registry_enabled_default = False @classmethod From 312c31afdac1f93abe7af5db3a196e92bef9da77 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Mon, 14 Feb 2022 02:49:19 -0500 Subject: [PATCH 05/19] revert change in vizio logic to fix bug (#66424) --- .../components/vizio/media_player.py | 22 ++++++------------- tests/components/vizio/test_media_player.py | 3 +-- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/vizio/media_player.py b/homeassistant/components/vizio/media_player.py index e9cd89c635a..664a8ae7da8 100644 --- a/homeassistant/components/vizio/media_player.py +++ b/homeassistant/components/vizio/media_player.py @@ -145,7 +145,7 @@ class VizioDevice(MediaPlayerEntity): self._volume_step = config_entry.options[CONF_VOLUME_STEP] self._current_input = None self._current_app_config = None - self._app_name = None + self._attr_app_name = None self._available_inputs = [] self._available_apps = [] self._all_apps = apps_coordinator.data if apps_coordinator else None @@ -209,7 +209,7 @@ class VizioDevice(MediaPlayerEntity): self._attr_volume_level = None self._attr_is_volume_muted = None self._current_input = None - self._app_name = None + self._attr_app_name = None self._current_app_config = None self._attr_sound_mode = None return @@ -265,13 +265,13 @@ class VizioDevice(MediaPlayerEntity): log_api_exception=False ) - self._app_name = find_app_name( + self._attr_app_name = find_app_name( self._current_app_config, [APP_HOME, *self._all_apps, *self._additional_app_configs], ) - if self._app_name == NO_APP_RUNNING: - self._app_name = None + if self._attr_app_name == NO_APP_RUNNING: + self._attr_app_name = None def _get_additional_app_names(self) -> list[dict[str, Any]]: """Return list of additional apps that were included in configuration.yaml.""" @@ -337,8 +337,8 @@ class VizioDevice(MediaPlayerEntity): @property def source(self) -> str | None: """Return current input of the device.""" - if self._app_name is not None and self._current_input in INPUT_APPS: - return self._app_name + if self._attr_app_name is not None and self._current_input in INPUT_APPS: + return self._attr_app_name return self._current_input @@ -364,14 +364,6 @@ class VizioDevice(MediaPlayerEntity): return self._available_inputs - @property - def app_name(self) -> str | None: - """Return the name of the current app.""" - if self.source == self._app_name: - return self._app_name - - return None - @property def app_id(self) -> str | None: """Return the ID of the current app if it is unknown by pyvizio.""" diff --git a/tests/components/vizio/test_media_player.py b/tests/components/vizio/test_media_player.py index d3ef4019c57..80f72280951 100644 --- a/tests/components/vizio/test_media_player.py +++ b/tests/components/vizio/test_media_player.py @@ -764,6 +764,5 @@ async def test_vizio_update_with_apps_on_input( ) await _add_config_entry_to_hass(hass, config_entry) attr = _get_attr_and_assert_base_attr(hass, DEVICE_CLASS_TV, STATE_ON) - # App name and app ID should not be in the attributes - assert "app_name" not in attr + # app ID should not be in the attributes assert "app_id" not in attr From 398f60c3d0d04da57c7daec5d17eef76466856d3 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 13 Feb 2022 07:09:37 -0800 Subject: [PATCH 06/19] Reset the stream backoff timeout when the url updates (#66426) Reset the stream backoff timeout when the url updates, meant to improve the retry behavior for nest cameras. The problem is the nest url updates faster than the stream reset time so the wait timeout never resets if there is a temporarily problem with the new url. In particular this *may* help with the flaky cloud nest urls, but seems more correct otherwise. --- homeassistant/components/stream/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/stream/__init__.py b/homeassistant/components/stream/__init__.py index 79506c0bda2..731fd410686 100644 --- a/homeassistant/components/stream/__init__.py +++ b/homeassistant/components/stream/__init__.py @@ -342,7 +342,9 @@ class Stream: stream_state.discontinuity() if not self.keepalive or self._thread_quit.is_set(): if self._fast_restart_once: - # The stream source is updated, restart without any delay. + # The stream source is updated, restart without any delay and reset the retry + # backoff for the new url. + wait_timeout = 0 self._fast_restart_once = False self._thread_quit.clear() continue From aad9992c9abd2d23c1b3965043590260ba4023fb Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Mon, 14 Feb 2022 15:30:04 +0200 Subject: [PATCH 07/19] Increase switcher_kis timeouts (#66465) --- homeassistant/components/switcher_kis/const.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/switcher_kis/const.py b/homeassistant/components/switcher_kis/const.py index 88b6e447446..fdd5b02fe9b 100644 --- a/homeassistant/components/switcher_kis/const.py +++ b/homeassistant/components/switcher_kis/const.py @@ -8,7 +8,7 @@ DATA_BRIDGE = "bridge" DATA_DEVICE = "device" DATA_DISCOVERY = "discovery" -DISCOVERY_TIME_SEC = 6 +DISCOVERY_TIME_SEC = 12 SIGNAL_DEVICE_ADD = "switcher_device_add" @@ -19,4 +19,4 @@ SERVICE_SET_AUTO_OFF_NAME = "set_auto_off" SERVICE_TURN_ON_WITH_TIMER_NAME = "turn_on_with_timer" # Defines the maximum interval device must send an update before it marked unavailable -MAX_UPDATE_INTERVAL_SEC = 20 +MAX_UPDATE_INTERVAL_SEC = 30 From f46fbcc9eb3d14bdba405ef544806159e3101824 Mon Sep 17 00:00:00 2001 From: Brett Adams Date: Sun, 6 Feb 2022 09:49:26 +1000 Subject: [PATCH 08/19] Bump Advantage Air to 0.2.6 (#65849) --- homeassistant/components/advantage_air/manifest.json | 10 +++------- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/advantage_air/manifest.json b/homeassistant/components/advantage_air/manifest.json index 6390ccea39c..2b72a97029b 100644 --- a/homeassistant/components/advantage_air/manifest.json +++ b/homeassistant/components/advantage_air/manifest.json @@ -3,12 +3,8 @@ "name": "Advantage Air", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/advantage_air", - "codeowners": [ - "@Bre77" - ], - "requirements": [ - "advantage_air==0.2.5" - ], + "codeowners": ["@Bre77"], + "requirements": ["advantage_air==0.2.6"], "quality_scale": "platinum", "iot_class": "local_polling" -} \ No newline at end of file +} diff --git a/requirements_all.txt b/requirements_all.txt index b524400e811..94d82ee3e68 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -114,7 +114,7 @@ adext==0.4.2 adguardhome==0.5.1 # homeassistant.components.advantage_air -advantage_air==0.2.5 +advantage_air==0.2.6 # homeassistant.components.frontier_silicon afsapi==0.0.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 358a1d21748..030043ffaaf 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -70,7 +70,7 @@ adext==0.4.2 adguardhome==0.5.1 # homeassistant.components.advantage_air -advantage_air==0.2.5 +advantage_air==0.2.6 # homeassistant.components.agent_dvr agent-py==0.0.23 From e5e3ab377dabd25460912ad1d97a4862fa0d0627 Mon Sep 17 00:00:00 2001 From: Brett Adams Date: Mon, 14 Feb 2022 22:16:05 +1000 Subject: [PATCH 09/19] Bump Advantage Air 0.3.0 (#66488) --- homeassistant/components/advantage_air/manifest.json | 8 ++++++-- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/advantage_air/manifest.json b/homeassistant/components/advantage_air/manifest.json index 2b72a97029b..56c89fe7346 100644 --- a/homeassistant/components/advantage_air/manifest.json +++ b/homeassistant/components/advantage_air/manifest.json @@ -3,8 +3,12 @@ "name": "Advantage Air", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/advantage_air", - "codeowners": ["@Bre77"], - "requirements": ["advantage_air==0.2.6"], + "codeowners": [ + "@Bre77" + ], + "requirements": [ + "advantage_air==0.3.0" + ], "quality_scale": "platinum", "iot_class": "local_polling" } diff --git a/requirements_all.txt b/requirements_all.txt index 94d82ee3e68..3d59317ac33 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -114,7 +114,7 @@ adext==0.4.2 adguardhome==0.5.1 # homeassistant.components.advantage_air -advantage_air==0.2.6 +advantage_air==0.3.0 # homeassistant.components.frontier_silicon afsapi==0.0.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 030043ffaaf..52e560c6115 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -70,7 +70,7 @@ adext==0.4.2 adguardhome==0.5.1 # homeassistant.components.advantage_air -advantage_air==0.2.6 +advantage_air==0.3.0 # homeassistant.components.agent_dvr agent-py==0.0.23 From 96cbae53939e2d676e1aa3466c6c4fa70ef4943d Mon Sep 17 00:00:00 2001 From: Diogo Gomes Date: Tue, 15 Feb 2022 01:16:30 +0000 Subject: [PATCH 10/19] Fix utility meter restore state (#66490) * Address #63874 * avoid setting _last_period to None * name is always set in discovery * ValueError never happens only DecimalException * async_tariff_change tracks state change - state machine will not pass a None * test we only reset one utility_meter * test corrupted restored state * pretty sure _current_tariff doesn't change from init until here * missing assert * Revert "async_tariff_change tracks state change - state machine will not pass a None" This reverts commit 24fc04a964139e5cfecbfa20f91e2d30ab145d77. * address review comment * always a Decimal --- .../components/utility_meter/__init__.py | 2 -- .../components/utility_meter/sensor.py | 19 ++++++++----------- tests/components/utility_meter/test_init.py | 11 ++++++++++- tests/components/utility_meter/test_sensor.py | 11 +++++++++-- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/utility_meter/__init__.py b/homeassistant/components/utility_meter/__init__.py index bf9beae060c..525b4f3b43c 100644 --- a/homeassistant/components/utility_meter/__init__.py +++ b/homeassistant/components/utility_meter/__init__.py @@ -182,8 +182,6 @@ class TariffSelect(RestoreEntity): async def async_added_to_hass(self): """Run when entity about to be added.""" await super().async_added_to_hass() - if self._current_tariff is not None: - return state = await self.async_get_last_state() if not state or state.state not in self._tariffs: diff --git a/homeassistant/components/utility_meter/sensor.py b/homeassistant/components/utility_meter/sensor.py index b65628d5f0b..ec137968bc5 100644 --- a/homeassistant/components/utility_meter/sensor.py +++ b/homeassistant/components/utility_meter/sensor.py @@ -32,6 +32,7 @@ from homeassistant.helpers.event import ( async_track_state_change_event, ) from homeassistant.helpers.restore_state import RestoreEntity +from homeassistant.helpers.template import is_number from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType import homeassistant.util.dt as dt_util @@ -166,13 +167,10 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity): self._parent_meter = parent_meter self._sensor_source_id = source_entity self._state = None - self._last_period = 0 + self._last_period = Decimal(0) self._last_reset = dt_util.utcnow() self._collecting = None - if name: - self._name = name - else: - self._name = f"{source_entity} meter" + self._name = name self._unit_of_measurement = None self._period = meter_type if meter_type is not None: @@ -231,8 +229,6 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity): return self._state += adjustment - except ValueError as err: - _LOGGER.warning("While processing state changes: %s", err) except DecimalException as err: _LOGGER.warning( "Invalid state (%s > %s): %s", old_state.state, new_state.state, err @@ -282,7 +278,7 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity): return _LOGGER.debug("Reset utility meter <%s>", self.entity_id) self._last_reset = dt_util.utcnow() - self._last_period = str(self._state) + self._last_period = Decimal(self._state) if self._state else Decimal(0) self._state = 0 self.async_write_ha_state() @@ -319,9 +315,10 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity): ATTR_UNIT_OF_MEASUREMENT ) self._last_period = ( - float(state.attributes.get(ATTR_LAST_PERIOD)) + Decimal(state.attributes[ATTR_LAST_PERIOD]) if state.attributes.get(ATTR_LAST_PERIOD) - else 0 + and is_number(state.attributes[ATTR_LAST_PERIOD]) + else Decimal(0) ) self._last_reset = dt_util.as_utc( dt_util.parse_datetime(state.attributes.get(ATTR_LAST_RESET)) @@ -399,7 +396,7 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity): state_attr = { ATTR_SOURCE_ID: self._sensor_source_id, ATTR_STATUS: PAUSED if self._collecting is None else COLLECTING, - ATTR_LAST_PERIOD: self._last_period, + ATTR_LAST_PERIOD: str(self._last_period), } if self._period is not None: state_attr[ATTR_PERIOD] = self._period diff --git a/tests/components/utility_meter/test_init.py b/tests/components/utility_meter/test_init.py index 61e6fc4dae8..3297c696ca1 100644 --- a/tests/components/utility_meter/test_init.py +++ b/tests/components/utility_meter/test_init.py @@ -62,7 +62,12 @@ async def test_services(hass): "source": "sensor.energy", "cycle": "hourly", "tariffs": ["peak", "offpeak"], - } + }, + "energy_bill2": { + "source": "sensor.energy", + "cycle": "hourly", + "tariffs": ["peak", "offpeak"], + }, } } @@ -153,6 +158,10 @@ async def test_services(hass): state = hass.states.get("sensor.energy_bill_offpeak") assert state.state == "0" + # meanwhile energy_bill2_peak accumulated all kWh + state = hass.states.get("sensor.energy_bill2_peak") + assert state.state == "4" + async def test_cron(hass, legacy_patchable_time): """Test cron pattern and offset fails.""" diff --git a/tests/components/utility_meter/test_sensor.py b/tests/components/utility_meter/test_sensor.py index 51212580aaf..fbaf795f9e2 100644 --- a/tests/components/utility_meter/test_sensor.py +++ b/tests/components/utility_meter/test_sensor.py @@ -304,6 +304,10 @@ async def test_restore_state(hass): ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR, }, ), + State( + "sensor.energy_bill_midpeak", + "error", + ), State( "sensor.energy_bill_offpeak", "6", @@ -326,6 +330,9 @@ async def test_restore_state(hass): assert state.attributes.get("last_reset") == last_reset assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR + state = hass.states.get("sensor.energy_bill_midpeak") + assert state.state == STATE_UNKNOWN + state = hass.states.get("sensor.energy_bill_offpeak") assert state.state == "6" assert state.attributes.get("status") == COLLECTING @@ -530,7 +537,7 @@ async def _test_self_reset(hass, config, start_time, expect_reset=True): assert state.attributes.get("last_reset") == now.isoformat() assert state.state == "3" else: - assert state.attributes.get("last_period") == 0 + assert state.attributes.get("last_period") == "0" assert state.state == "5" start_time_str = dt_util.parse_datetime(start_time).isoformat() assert state.attributes.get("last_reset") == start_time_str @@ -559,7 +566,7 @@ async def _test_self_reset(hass, config, start_time, expect_reset=True): assert state.attributes.get("last_period") == "2" assert state.state == "7" else: - assert state.attributes.get("last_period") == 0 + assert state.attributes.get("last_period") == "0" assert state.state == "9" From 5cccac7a19f02c8b72f030ea3e0fe4deb01d6b0a Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 14 Feb 2022 12:43:36 +0100 Subject: [PATCH 11/19] Fix access to hass.data in hdmi-cec (#66504) Co-authored-by: epenet --- homeassistant/components/hdmi_cec/media_player.py | 8 ++++---- homeassistant/components/hdmi_cec/switch.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/hdmi_cec/media_player.py b/homeassistant/components/hdmi_cec/media_player.py index c31daa85316..9ee705c1c5e 100644 --- a/homeassistant/components/hdmi_cec/media_player.py +++ b/homeassistant/components/hdmi_cec/media_player.py @@ -26,7 +26,7 @@ from pycec.const import ( from homeassistant.components.media_player import MediaPlayerEntity from homeassistant.components.media_player.const import ( - DOMAIN, + DOMAIN as MP_DOMAIN, SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY_MEDIA, @@ -48,11 +48,11 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import ATTR_NEW, CecEntity +from . import ATTR_NEW, DOMAIN, CecEntity _LOGGER = logging.getLogger(__name__) -ENTITY_ID_FORMAT = DOMAIN + ".{}" +ENTITY_ID_FORMAT = MP_DOMAIN + ".{}" def setup_platform( @@ -77,7 +77,7 @@ class CecPlayerEntity(CecEntity, MediaPlayerEntity): def __init__(self, device, logical) -> None: """Initialize the HDMI device.""" CecEntity.__init__(self, device, logical) - self.entity_id = f"{DOMAIN}.hdmi_{hex(self._logical_address)[2:]}" + self.entity_id = f"{MP_DOMAIN}.hdmi_{hex(self._logical_address)[2:]}" def send_keypress(self, key): """Send keypress to CEC adapter.""" diff --git a/homeassistant/components/hdmi_cec/switch.py b/homeassistant/components/hdmi_cec/switch.py index 8e6deae1394..a5d64b2a7fa 100644 --- a/homeassistant/components/hdmi_cec/switch.py +++ b/homeassistant/components/hdmi_cec/switch.py @@ -3,17 +3,17 @@ from __future__ import annotations import logging -from homeassistant.components.switch import DOMAIN, SwitchEntity +from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN, SwitchEntity from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from . import ATTR_NEW, CecEntity +from . import ATTR_NEW, DOMAIN, CecEntity _LOGGER = logging.getLogger(__name__) -ENTITY_ID_FORMAT = DOMAIN + ".{}" +ENTITY_ID_FORMAT = SWITCH_DOMAIN + ".{}" def setup_platform( @@ -38,7 +38,7 @@ class CecSwitchEntity(CecEntity, SwitchEntity): def __init__(self, device, logical) -> None: """Initialize the HDMI device.""" CecEntity.__init__(self, device, logical) - self.entity_id = f"{DOMAIN}.hdmi_{hex(self._logical_address)[2:]}" + self.entity_id = f"{SWITCH_DOMAIN}.hdmi_{hex(self._logical_address)[2:]}" def turn_on(self, **kwargs) -> None: """Turn device on.""" From 6472cb8721851a09f597da7e661159b88e8917d1 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 15 Feb 2022 01:12:34 +0100 Subject: [PATCH 12/19] Revert "Fix raspihats callbacks (#64122)" (#66517) Co-authored-by: epenet --- .../components/raspihats/binary_sensor.py | 20 +++--------- homeassistant/components/raspihats/switch.py | 32 +++++++------------ 2 files changed, 15 insertions(+), 37 deletions(-) diff --git a/homeassistant/components/raspihats/binary_sensor.py b/homeassistant/components/raspihats/binary_sensor.py index 2c0ce10a5f3..f8fbc0d010f 100644 --- a/homeassistant/components/raspihats/binary_sensor.py +++ b/homeassistant/components/raspihats/binary_sensor.py @@ -2,7 +2,6 @@ from __future__ import annotations import logging -from typing import TYPE_CHECKING import voluptuous as vol @@ -109,20 +108,12 @@ class I2CHatBinarySensor(BinarySensorEntity): self._device_class = device_class self._state = self.I2C_HATS_MANAGER.read_di(self._address, self._channel) - async def async_added_to_hass(self) -> None: - """Register callbacks.""" - if TYPE_CHECKING: - assert self.I2C_HATS_MANAGER - def online_callback(): """Call fired when board is online.""" self.schedule_update_ha_state() - await self.hass.async_add_executor_job( - self.I2C_HATS_MANAGER.register_online_callback, - self._address, - self._channel, - online_callback, + self.I2C_HATS_MANAGER.register_online_callback( + self._address, self._channel, online_callback ) def edge_callback(state): @@ -130,11 +121,8 @@ class I2CHatBinarySensor(BinarySensorEntity): self._state = state self.schedule_update_ha_state() - await self.hass.async_add_executor_job( - self.I2C_HATS_MANAGER.register_di_callback, - self._address, - self._channel, - edge_callback, + self.I2C_HATS_MANAGER.register_di_callback( + self._address, self._channel, edge_callback ) @property diff --git a/homeassistant/components/raspihats/switch.py b/homeassistant/components/raspihats/switch.py index 0e05e376ed4..8ca88528543 100644 --- a/homeassistant/components/raspihats/switch.py +++ b/homeassistant/components/raspihats/switch.py @@ -2,7 +2,6 @@ from __future__ import annotations import logging -from typing import TYPE_CHECKING import voluptuous as vol @@ -101,7 +100,6 @@ class I2CHatSwitch(SwitchEntity): self._channel = channel self._name = name or DEVICE_DEFAULT_NAME self._invert_logic = invert_logic - self._state = initial_state if initial_state is not None: if self._invert_logic: state = not initial_state @@ -109,27 +107,14 @@ class I2CHatSwitch(SwitchEntity): state = initial_state self.I2C_HATS_MANAGER.write_dq(self._address, self._channel, state) - async def async_added_to_hass(self) -> None: - """Register callbacks.""" - if TYPE_CHECKING: - assert self.I2C_HATS_MANAGER + def online_callback(): + """Call fired when board is online.""" + self.schedule_update_ha_state() - await self.hass.async_add_executor_job( - self.I2C_HATS_MANAGER.register_online_callback, - self._address, - self._channel, - self.online_callback, + self.I2C_HATS_MANAGER.register_online_callback( + self._address, self._channel, online_callback ) - def online_callback(self): - """Call fired when board is online.""" - try: - self._state = self.I2C_HATS_MANAGER.read_dq(self._address, self._channel) - except I2CHatsException as ex: - _LOGGER.error(self._log_message(f"Is ON check failed, {ex!s}")) - self._state = False - self.schedule_update_ha_state() - def _log_message(self, message): """Create log message.""" string = f"{self._name} " @@ -150,7 +135,12 @@ class I2CHatSwitch(SwitchEntity): @property def is_on(self): """Return true if device is on.""" - return self._state != self._invert_logic + try: + state = self.I2C_HATS_MANAGER.read_dq(self._address, self._channel) + return state != self._invert_logic + except I2CHatsException as ex: + _LOGGER.error(self._log_message(f"Is ON check failed, {ex!s}")) + return False def turn_on(self, **kwargs): """Turn the device on.""" From 104f56a01d2ddc3e451e7bca5cf65f1241894b64 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Feb 2022 13:14:45 -0600 Subject: [PATCH 13/19] Fix flux_led turn on with slow responding devices (#66527) --- homeassistant/components/flux_led/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json index 40661f27bab..17c73200619 100644 --- a/homeassistant/components/flux_led/manifest.json +++ b/homeassistant/components/flux_led/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["network"], "documentation": "https://www.home-assistant.io/integrations/flux_led", - "requirements": ["flux_led==0.28.22"], + "requirements": ["flux_led==0.28.26"], "quality_scale": "platinum", "codeowners": ["@icemanch", "@bdraco"], "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index 3d59317ac33..789721caa4a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -681,7 +681,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.28.22 +flux_led==0.28.26 # homeassistant.components.homekit fnvhash==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 52e560c6115..da71e776f55 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -427,7 +427,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.28.22 +flux_led==0.28.26 # homeassistant.components.homekit fnvhash==0.1.0 From addc6bce63aac09010b257d1808abf91d3ac07dc Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Tue, 15 Feb 2022 22:42:18 +0100 Subject: [PATCH 14/19] Add fallback for serialnumber (#66553) --- homeassistant/components/philips_js/__init__.py | 9 ++++++--- homeassistant/components/philips_js/config_flow.py | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/philips_js/__init__.py b/homeassistant/components/philips_js/__init__.py index 1292310f134..9a317726768 100644 --- a/homeassistant/components/philips_js/__init__.py +++ b/homeassistant/components/philips_js/__init__.py @@ -148,9 +148,12 @@ class PhilipsTVDataUpdateCoordinator(DataUpdateCoordinator[None]): @property def unique_id(self) -> str: """Return the system descriptor.""" - assert self.config_entry - assert self.config_entry.unique_id - return self.config_entry.unique_id + entry: ConfigEntry = self.config_entry + assert entry + if entry.unique_id: + return entry.unique_id + assert entry.entry_id + return entry.entry_id @property def _notify_wanted(self): diff --git a/homeassistant/components/philips_js/config_flow.py b/homeassistant/components/philips_js/config_flow.py index 89f13ffadbf..29abbe5dd71 100644 --- a/homeassistant/components/philips_js/config_flow.py +++ b/homeassistant/components/philips_js/config_flow.py @@ -122,9 +122,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): LOGGER.exception("Unexpected exception") errors["base"] = "unknown" else: - - await self.async_set_unique_id(hub.system["serialnumber"]) - self._abort_if_unique_id_configured() + if serialnumber := hub.system.get("serialnumber"): + await self.async_set_unique_id(serialnumber) + self._abort_if_unique_id_configured() self._current[CONF_SYSTEM] = hub.system self._current[CONF_API_VERSION] = hub.api_version From 8b39866bb765b5a7670b8166ffd1e6f55d95fec1 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 15 Feb 2022 15:25:36 +0100 Subject: [PATCH 15/19] Fix Tuya Covers without state in their control data point (#66564) --- homeassistant/components/tuya/cover.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/tuya/cover.py b/homeassistant/components/tuya/cover.py index 6a9e3767065..de277e61510 100644 --- a/homeassistant/components/tuya/cover.py +++ b/homeassistant/components/tuya/cover.py @@ -158,7 +158,10 @@ async def async_setup_entry( device = hass_data.device_manager.device_map[device_id] if descriptions := COVERS.get(device.category): for description in descriptions: - if description.key in device.status: + if ( + description.key in device.function + or description.key in device.status_range + ): entities.append( TuyaCoverEntity( device, hass_data.device_manager, description From 7bb14aae23c306da1043f88a99a999147ef2f9aa Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Tue, 15 Feb 2022 13:43:31 -0800 Subject: [PATCH 16/19] Override and disable nest stream `unavailable` behavior (#66571) --- homeassistant/components/nest/camera_sdm.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/homeassistant/components/nest/camera_sdm.py b/homeassistant/components/nest/camera_sdm.py index fca79bde040..858561dd3ea 100644 --- a/homeassistant/components/nest/camera_sdm.py +++ b/homeassistant/components/nest/camera_sdm.py @@ -127,6 +127,15 @@ class NestCamera(Camera): return STREAM_TYPE_WEB_RTC return super().frontend_stream_type + @property + def available(self) -> bool: + """Return True if entity is available.""" + # Cameras are marked unavailable on stream errors in #54659 however nest streams have + # a high error rate (#60353). Given nest streams are so flaky, marking the stream + # unavailable has other side effects like not showing the camera image which sometimes + # are still able to work. Until the streams are fixed, just leave the streams as available. + return True + async def stream_source(self) -> str | None: """Return the source of the stream.""" if not self.supported_features & SUPPORT_STREAM: From de96d21ea91e9a32f545937e2cb6b085257f259e Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Wed, 16 Feb 2022 02:26:13 +0100 Subject: [PATCH 17/19] Bump aiohue to version 4.1.2 (#66609) --- homeassistant/components/hue/manifest.json | 2 +- homeassistant/components/hue/scene.py | 8 +++- homeassistant/components/hue/switch.py | 10 ++++- .../components/hue/v2/binary_sensor.py | 12 +++--- homeassistant/components/hue/v2/entity.py | 19 +++++++-- homeassistant/components/hue/v2/group.py | 4 +- homeassistant/components/hue/v2/sensor.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/hue/conftest.py | 3 +- tests/components/hue/test_light_v2.py | 39 +++++++++++++++---- tests/components/hue/test_switch.py | 7 +++- 12 files changed, 80 insertions(+), 30 deletions(-) diff --git a/homeassistant/components/hue/manifest.json b/homeassistant/components/hue/manifest.json index 12f7df13866..a21a3ace72b 100644 --- a/homeassistant/components/hue/manifest.json +++ b/homeassistant/components/hue/manifest.json @@ -3,7 +3,7 @@ "name": "Philips Hue", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/hue", - "requirements": ["aiohue==4.0.1"], + "requirements": ["aiohue==4.1.2"], "ssdp": [ { "manufacturer": "Royal Philips Electronics", diff --git a/homeassistant/components/hue/scene.py b/homeassistant/components/hue/scene.py index 3d1967ecff3..8e894b4e295 100644 --- a/homeassistant/components/hue/scene.py +++ b/homeassistant/components/hue/scene.py @@ -5,7 +5,11 @@ from typing import Any from aiohue.v2 import HueBridgeV2 from aiohue.v2.controllers.events import EventType -from aiohue.v2.controllers.scenes import Scene as HueScene, ScenesController +from aiohue.v2.controllers.scenes import ( + Scene as HueScene, + ScenePut as HueScenePut, + ScenesController, +) import voluptuous as vol from homeassistant.components.scene import ATTR_TRANSITION, Scene as SceneEntity @@ -131,7 +135,7 @@ class HueSceneEntity(HueBaseEntity, SceneEntity): await self.bridge.async_request_call( self.controller.update, self.resource.id, - HueScene(self.resource.id, speed=speed / 100), + HueScenePut(speed=speed / 100), ) await self.bridge.async_request_call( diff --git a/homeassistant/components/hue/switch.py b/homeassistant/components/hue/switch.py index 7f8e048d692..7fb40cba38f 100644 --- a/homeassistant/components/hue/switch.py +++ b/homeassistant/components/hue/switch.py @@ -5,8 +5,12 @@ from typing import Any, Union from aiohue.v2 import HueBridgeV2 from aiohue.v2.controllers.events import EventType -from aiohue.v2.controllers.sensors import LightLevelController, MotionController -from aiohue.v2.models.resource import SensingService +from aiohue.v2.controllers.sensors import ( + LightLevel, + LightLevelController, + Motion, + MotionController, +) from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity from homeassistant.config_entries import ConfigEntry @@ -20,6 +24,8 @@ from .v2.entity import HueBaseEntity ControllerType = Union[LightLevelController, MotionController] +SensingService = Union[LightLevel, Motion] + async def async_setup_entry( hass: HomeAssistant, diff --git a/homeassistant/components/hue/v2/binary_sensor.py b/homeassistant/components/hue/v2/binary_sensor.py index 47617b45af6..a7077ccf765 100644 --- a/homeassistant/components/hue/v2/binary_sensor.py +++ b/homeassistant/components/hue/v2/binary_sensor.py @@ -4,13 +4,13 @@ from __future__ import annotations from typing import Any, Union from aiohue.v2 import HueBridgeV2 -from aiohue.v2.controllers.config import EntertainmentConfigurationController +from aiohue.v2.controllers.config import ( + EntertainmentConfiguration, + EntertainmentConfigurationController, +) from aiohue.v2.controllers.events import EventType from aiohue.v2.controllers.sensors import MotionController -from aiohue.v2.models.entertainment import ( - EntertainmentConfiguration, - EntertainmentStatus, -) +from aiohue.v2.models.entertainment_configuration import EntertainmentStatus from aiohue.v2.models.motion import Motion from homeassistant.components.binary_sensor import ( @@ -109,4 +109,4 @@ class HueEntertainmentActiveSensor(HueBinarySensorBase): def name(self) -> str: """Return sensor name.""" type_title = self.resource.type.value.replace("_", " ").title() - return f"{self.resource.name}: {type_title}" + return f"{self.resource.metadata.name}: {type_title}" diff --git a/homeassistant/components/hue/v2/entity.py b/homeassistant/components/hue/v2/entity.py index c8c2f9e423b..721425606bc 100644 --- a/homeassistant/components/hue/v2/entity.py +++ b/homeassistant/components/hue/v2/entity.py @@ -1,11 +1,12 @@ """Generic Hue Entity Model.""" from __future__ import annotations +from typing import TYPE_CHECKING, Union + from aiohue.v2.controllers.base import BaseResourcesController from aiohue.v2.controllers.events import EventType -from aiohue.v2.models.clip import CLIPResource -from aiohue.v2.models.connectivity import ConnectivityServiceStatus from aiohue.v2.models.resource import ResourceTypes +from aiohue.v2.models.zigbee_connectivity import ConnectivityServiceStatus from homeassistant.core import callback from homeassistant.helpers.entity import DeviceInfo, Entity @@ -14,6 +15,16 @@ from homeassistant.helpers.entity_registry import async_get as async_get_entity_ from ..bridge import HueBridge from ..const import CONF_IGNORE_AVAILABILITY, DOMAIN +if TYPE_CHECKING: + from aiohue.v2.models.device_power import DevicePower + from aiohue.v2.models.grouped_light import GroupedLight + from aiohue.v2.models.light import Light + from aiohue.v2.models.light_level import LightLevel + from aiohue.v2.models.motion import Motion + + HueResource = Union[Light, DevicePower, GroupedLight, LightLevel, Motion] + + RESOURCE_TYPE_NAMES = { # a simple mapping of hue resource type to Hass name ResourceTypes.LIGHT_LEVEL: "Illuminance", @@ -30,7 +41,7 @@ class HueBaseEntity(Entity): self, bridge: HueBridge, controller: BaseResourcesController, - resource: CLIPResource, + resource: HueResource, ) -> None: """Initialize a generic Hue resource entity.""" self.bridge = bridge @@ -122,7 +133,7 @@ class HueBaseEntity(Entity): # used in subclasses @callback - def _handle_event(self, event_type: EventType, resource: CLIPResource) -> None: + def _handle_event(self, event_type: EventType, resource: HueResource) -> None: """Handle status event for this resource (or it's parent).""" if event_type == EventType.RESOURCE_DELETED and resource.id == self.resource.id: self.logger.debug("Received delete for %s", self.entity_id) diff --git a/homeassistant/components/hue/v2/group.py b/homeassistant/components/hue/v2/group.py index 300f08727ba..31c5a502853 100644 --- a/homeassistant/components/hue/v2/group.py +++ b/homeassistant/components/hue/v2/group.py @@ -7,7 +7,7 @@ from typing import Any from aiohue.v2 import HueBridgeV2 from aiohue.v2.controllers.events import EventType from aiohue.v2.controllers.groups import GroupedLight, Room, Zone -from aiohue.v2.models.feature import DynamicsFeatureStatus +from aiohue.v2.models.feature import DynamicStatus from homeassistant.components.light import ( ATTR_BRIGHTNESS, @@ -283,7 +283,7 @@ class GroupedHueLight(HueBaseEntity, LightEntity): total_brightness += dimming.brightness if ( light.dynamics - and light.dynamics.status == DynamicsFeatureStatus.DYNAMIC_PALETTE + and light.dynamics.status == DynamicStatus.DYNAMIC_PALETTE ): lights_in_dynamic_mode += 1 diff --git a/homeassistant/components/hue/v2/sensor.py b/homeassistant/components/hue/v2/sensor.py index ff2b7b78e7d..d331393d29b 100644 --- a/homeassistant/components/hue/v2/sensor.py +++ b/homeassistant/components/hue/v2/sensor.py @@ -12,10 +12,10 @@ from aiohue.v2.controllers.sensors import ( TemperatureController, ZigbeeConnectivityController, ) -from aiohue.v2.models.connectivity import ZigbeeConnectivity from aiohue.v2.models.device_power import DevicePower from aiohue.v2.models.light_level import LightLevel from aiohue.v2.models.temperature import Temperature +from aiohue.v2.models.zigbee_connectivity import ZigbeeConnectivity from homeassistant.components.binary_sensor import BinarySensorDeviceClass from homeassistant.components.sensor import ( diff --git a/requirements_all.txt b/requirements_all.txt index 789721caa4a..e94d0c1acae 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -191,7 +191,7 @@ aiohomekit==0.6.11 aiohttp_cors==0.7.0 # homeassistant.components.hue -aiohue==4.0.1 +aiohue==4.1.2 # homeassistant.components.homewizard aiohwenergy==0.8.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index da71e776f55..8985459f394 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -141,7 +141,7 @@ aiohomekit==0.6.11 aiohttp_cors==0.7.0 # homeassistant.components.hue -aiohue==4.0.1 +aiohue==4.1.2 # homeassistant.components.homewizard aiohwenergy==0.8.0 diff --git a/tests/components/hue/conftest.py b/tests/components/hue/conftest.py index 9e9ed9af31b..d0d15d320e0 100644 --- a/tests/components/hue/conftest.py +++ b/tests/components/hue/conftest.py @@ -8,7 +8,6 @@ from unittest.mock import AsyncMock, Mock, patch import aiohue.v1 as aiohue_v1 import aiohue.v2 as aiohue_v2 from aiohue.v2.controllers.events import EventType -from aiohue.v2.models.clip import parse_clip_resource import pytest from homeassistant.components import hue @@ -187,7 +186,7 @@ def create_mock_api_v2(hass): def emit_event(event_type, data): """Emit an event from a (hue resource) dict.""" - api.events.emit(EventType(event_type), parse_clip_resource(data)) + api.events.emit(EventType(event_type), data) api.load_test_data = load_test_data api.emit_event = emit_event diff --git a/tests/components/hue/test_light_v2.py b/tests/components/hue/test_light_v2.py index c7578df3a49..f0265233e4e 100644 --- a/tests/components/hue/test_light_v2.py +++ b/tests/components/hue/test_light_v2.py @@ -97,8 +97,12 @@ async def test_light_turn_on_service(hass, mock_bridge_v2, v2_resources_test_dat assert mock_bridge_v2.mock_requests[0]["json"]["color_temperature"]["mirek"] == 300 # Now generate update event by emitting the json we've sent as incoming event - mock_bridge_v2.mock_requests[0]["json"]["color_temperature"].pop("mirek_valid") - mock_bridge_v2.api.emit_event("update", mock_bridge_v2.mock_requests[0]["json"]) + event = { + "id": "3a6710fa-4474-4eba-b533-5e6e72968feb", + "type": "light", + **mock_bridge_v2.mock_requests[0]["json"], + } + mock_bridge_v2.api.emit_event("update", event) await hass.async_block_till_done() # the light should now be on @@ -186,7 +190,12 @@ async def test_light_turn_off_service(hass, mock_bridge_v2, v2_resources_test_da assert mock_bridge_v2.mock_requests[0]["json"]["on"]["on"] is False # Now generate update event by emitting the json we've sent as incoming event - mock_bridge_v2.api.emit_event("update", mock_bridge_v2.mock_requests[0]["json"]) + event = { + "id": "02cba059-9c2c-4d45-97e4-4f79b1bfbaa1", + "type": "light", + **mock_bridge_v2.mock_requests[0]["json"], + } + mock_bridge_v2.api.emit_event("update", event) await hass.async_block_till_done() # the light should now be off @@ -377,10 +386,20 @@ async def test_grouped_lights(hass, mock_bridge_v2, v2_resources_test_data): ) # Now generate update events by emitting the json we've sent as incoming events - for index in range(0, 3): - mock_bridge_v2.api.emit_event( - "update", mock_bridge_v2.mock_requests[index]["json"] - ) + for index, light_id in enumerate( + [ + "02cba059-9c2c-4d45-97e4-4f79b1bfbaa1", + "b3fe71ef-d0ef-48de-9355-d9e604377df0", + "8015b17f-8336-415b-966a-b364bd082397", + ] + ): + event = { + "id": light_id, + "type": "light", + **mock_bridge_v2.mock_requests[index]["json"], + } + mock_bridge_v2.api.emit_event("update", event) + await hass.async_block_till_done() await hass.async_block_till_done() # the light should now be on and have the properties we've set @@ -406,6 +425,12 @@ async def test_grouped_lights(hass, mock_bridge_v2, v2_resources_test_data): assert mock_bridge_v2.mock_requests[0]["json"]["on"]["on"] is False # Now generate update event by emitting the json we've sent as incoming event + event = { + "id": "f2416154-9607-43ab-a684-4453108a200e", + "type": "grouped_light", + **mock_bridge_v2.mock_requests[0]["json"], + } + mock_bridge_v2.api.emit_event("update", event) mock_bridge_v2.api.emit_event("update", mock_bridge_v2.mock_requests[0]["json"]) await hass.async_block_till_done() diff --git a/tests/components/hue/test_switch.py b/tests/components/hue/test_switch.py index 257f1a253c3..e8086709705 100644 --- a/tests/components/hue/test_switch.py +++ b/tests/components/hue/test_switch.py @@ -69,7 +69,12 @@ async def test_switch_turn_off_service(hass, mock_bridge_v2, v2_resources_test_d assert mock_bridge_v2.mock_requests[0]["json"]["enabled"] is False # Now generate update event by emitting the json we've sent as incoming event - mock_bridge_v2.api.emit_event("update", mock_bridge_v2.mock_requests[0]["json"]) + event = { + "id": "b6896534-016d-4052-8cb4-ef04454df62c", + "type": "motion", + **mock_bridge_v2.mock_requests[0]["json"], + } + mock_bridge_v2.api.emit_event("update", event) await hass.async_block_till_done() # the switch should now be off From 6aa99d10638f2325671040c239dc698237e93a4c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 15 Feb 2022 17:47:46 -0800 Subject: [PATCH 18/19] Bumped version to 2022.2.7 --- homeassistant/const.py | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 13250b3f9e5..c206a4f3f7a 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 = 2 -PATCH_VERSION: Final = "6" +PATCH_VERSION: Final = "7" __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/setup.cfg b/setup.cfg index c912d68ed55..a9634ea3b6f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = homeassistant -version = 2022.2.6 +version = 2022.2.7 author = The Home Assistant Authors author_email = hello@home-assistant.io license = Apache-2.0 From 7d2e42d026d9da542808d01beec57d6e6522838b Mon Sep 17 00:00:00 2001 From: jjlawren Date: Tue, 15 Feb 2022 20:02:45 -0600 Subject: [PATCH 19/19] Backport #66399 to rc (#66625) --- homeassistant/components/sonos/helpers.py | 2 +- homeassistant/components/sonos/switch.py | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/sonos/helpers.py b/homeassistant/components/sonos/helpers.py index 625b54b941e..6da25314f0f 100644 --- a/homeassistant/components/sonos/helpers.py +++ b/homeassistant/components/sonos/helpers.py @@ -23,7 +23,7 @@ UID_POSTFIX = "01400" _LOGGER = logging.getLogger(__name__) -_T = TypeVar("_T", "SonosSpeaker", "SonosEntity") +_T = TypeVar("_T", bound="SonosSpeaker | SonosEntity") _R = TypeVar("_R") _P = ParamSpec("_P") diff --git a/homeassistant/components/sonos/switch.py b/homeassistant/components/sonos/switch.py index 4e3303db45d..2ee8af61327 100644 --- a/homeassistant/components/sonos/switch.py +++ b/homeassistant/components/sonos/switch.py @@ -3,6 +3,7 @@ from __future__ import annotations import datetime import logging +from typing import Any from soco.exceptions import SoCoException, SoCoSlaveException, SoCoUPnPException @@ -342,20 +343,20 @@ class SonosAlarmEntity(SonosEntity, SwitchEntity): ATTR_INCLUDE_LINKED_ZONES: self.alarm.include_linked_zones, } - async def async_turn_on(self, **kwargs) -> None: + def turn_on(self, **kwargs: Any) -> None: """Turn alarm switch on.""" - await self.async_handle_switch_on_off(turn_on=True) + self._handle_switch_on_off(turn_on=True) - async def async_turn_off(self, **kwargs) -> None: + def turn_off(self, **kwargs: Any) -> None: """Turn alarm switch off.""" - await self.async_handle_switch_on_off(turn_on=False) + self._handle_switch_on_off(turn_on=False) - async def async_handle_switch_on_off(self, turn_on: bool) -> None: + def _handle_switch_on_off(self, turn_on: bool) -> None: """Handle turn on/off of alarm switch.""" try: _LOGGER.debug("Toggling the state of %s", self.entity_id) self.alarm.enabled = turn_on - await self.hass.async_add_executor_job(self.alarm.save) + self.alarm.save() except (OSError, SoCoException, SoCoUPnPException) as exc: _LOGGER.error("Could not update %s: %s", self.entity_id, exc)