From 5a03fffc20c11c2e05926ffc4b977aa9cf665e07 Mon Sep 17 00:00:00 2001 From: sindudas Date: Fri, 17 Dec 2021 09:34:27 +0100 Subject: [PATCH 01/18] Update ebusdpy version (#59899) --- homeassistant/components/ebusd/manifest.json | 2 +- requirements_all.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/ebusd/manifest.json b/homeassistant/components/ebusd/manifest.json index 347fee0bc85..390e8efe7d5 100644 --- a/homeassistant/components/ebusd/manifest.json +++ b/homeassistant/components/ebusd/manifest.json @@ -2,7 +2,7 @@ "domain": "ebusd", "name": "ebusd", "documentation": "https://www.home-assistant.io/integrations/ebusd", - "requirements": ["ebusdpy==0.0.16"], + "requirements": ["ebusdpy==0.0.17"], "codeowners": [], "iot_class": "local_polling" } diff --git a/requirements_all.txt b/requirements_all.txt index 0631714600b..c0048a7cafc 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -573,7 +573,7 @@ dweepy==0.3.0 dynalite_devices==0.1.46 # homeassistant.components.ebusd -ebusdpy==0.0.16 +ebusdpy==0.0.17 # homeassistant.components.ecoal_boiler ecoaliface==0.4.0 From 499cc2e51d6411498284bf81f0a37de56a034e36 Mon Sep 17 00:00:00 2001 From: Ian Date: Fri, 17 Dec 2021 00:39:55 -0800 Subject: [PATCH 02/18] Nextbus upcoming sort as integer (#61416) --- homeassistant/components/nextbus/sensor.py | 2 +- tests/components/nextbus/test_sensor.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/nextbus/sensor.py b/homeassistant/components/nextbus/sensor.py index 3756c1853b7..815875df0ca 100644 --- a/homeassistant/components/nextbus/sensor.py +++ b/homeassistant/components/nextbus/sensor.py @@ -214,7 +214,7 @@ class NextBusDepartureSensor(SensorEntity): # Generate list of upcoming times self._attributes["upcoming"] = ", ".join( - sorted(p["minutes"] for p in predictions) + sorted((p["minutes"] for p in predictions), key=int) ) latest_prediction = maybe_first(predictions) diff --git a/tests/components/nextbus/test_sensor.py b/tests/components/nextbus/test_sensor.py index 016afed2b0f..f113d5c83da 100644 --- a/tests/components/nextbus/test_sensor.py +++ b/tests/components/nextbus/test_sensor.py @@ -40,6 +40,7 @@ BASIC_RESULTS = { {"minutes": "1", "epochTime": "1553807371000"}, {"minutes": "2", "epochTime": "1553807372000"}, {"minutes": "3", "epochTime": "1553807373000"}, + {"minutes": "10", "epochTime": "1553807380000"}, ], }, } @@ -128,7 +129,7 @@ async def test_verify_valid_state( assert state.attributes["route"] == VALID_ROUTE_TITLE assert state.attributes["stop"] == VALID_STOP_TITLE assert state.attributes["direction"] == "Outbound" - assert state.attributes["upcoming"] == "1, 2, 3" + assert state.attributes["upcoming"] == "1, 2, 3, 10" async def test_message_dict( From c73319e162e3403439b21b051004768046a2efc6 Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Thu, 16 Dec 2021 14:08:40 +0100 Subject: [PATCH 03/18] Add restore logic to Shelly climate platform (#61632) * Add restore logic to Shelly climate platform * Handle missing channel on restore --- homeassistant/components/shelly/__init__.py | 3 +- homeassistant/components/shelly/climate.py | 198 +++++++++++++++++--- 2 files changed, 172 insertions(+), 29 deletions(-) diff --git a/homeassistant/components/shelly/__init__.py b/homeassistant/components/shelly/__init__.py index 4109130ab80..27f25211a96 100644 --- a/homeassistant/components/shelly/__init__.py +++ b/homeassistant/components/shelly/__init__.py @@ -68,13 +68,12 @@ from .utils import ( BLOCK_PLATFORMS: Final = [ "binary_sensor", "button", - "climate", "cover", "light", "sensor", "switch", ] -BLOCK_SLEEPING_PLATFORMS: Final = ["binary_sensor", "sensor"] +BLOCK_SLEEPING_PLATFORMS: Final = ["binary_sensor", "climate", "sensor"] RPC_PLATFORMS: Final = ["binary_sensor", "button", "light", "sensor", "switch"] _LOGGER: Final = logging.getLogger(__name__) diff --git a/homeassistant/components/shelly/climate.py b/homeassistant/components/shelly/climate.py index f2db157ecf2..af84e881447 100644 --- a/homeassistant/components/shelly/climate.py +++ b/homeassistant/components/shelly/climate.py @@ -3,12 +3,13 @@ from __future__ import annotations import asyncio import logging +from types import MappingProxyType from typing import Any, Final, cast from aioshelly.block_device import Block import async_timeout -from homeassistant.components.climate import ClimateEntity +from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN, ClimateEntity from homeassistant.components.climate.const import ( CURRENT_HVAC_HEAT, CURRENT_HVAC_IDLE, @@ -20,11 +21,12 @@ from homeassistant.components.climate.const import ( SUPPORT_TARGET_TEMPERATURE, ) from homeassistant.components.shelly import BlockDeviceWrapper -from homeassistant.components.shelly.entity import ShellyBlockEntity from homeassistant.components.shelly.utils import get_device_entry_gen from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS -from homeassistant.core import HomeAssistant +from homeassistant.core import HomeAssistant, State, callback +from homeassistant.helpers import device_registry, entity, entity_registry +from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.restore_state import RestoreEntity @@ -49,10 +51,30 @@ async def async_setup_entry( if get_device_entry_gen(config_entry) == 2: return + wrapper: BlockDeviceWrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][ + config_entry.entry_id + ][BLOCK] + + if wrapper.device.initialized: + await async_setup_climate_entities(async_add_entities, wrapper) + else: + await async_restore_climate_entities( + hass, config_entry, async_add_entities, wrapper + ) + + +async def async_setup_climate_entities( + async_add_entities: AddEntitiesCallback, + wrapper: BlockDeviceWrapper, +) -> None: + """Set up online climate devices.""" + + _LOGGER.info("Setup online climate device %s", wrapper.name) device_block: Block | None = None sensor_block: Block | None = None - wrapper = hass.data[DOMAIN][DATA_CONFIG_ENTRY][config_entry.entry_id][BLOCK] + assert wrapper.device.blocks + for block in wrapper.device.blocks: if block.type == "device": device_block = block @@ -60,10 +82,37 @@ async def async_setup_entry( sensor_block = block if sensor_block and device_block: - async_add_entities([ShellyClimate(wrapper, sensor_block, device_block)]) + async_add_entities([BlockSleepingClimate(wrapper, sensor_block, device_block)]) -class ShellyClimate(ShellyBlockEntity, RestoreEntity, ClimateEntity): +async def async_restore_climate_entities( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, + wrapper: BlockDeviceWrapper, +) -> None: + """Restore sleeping climate devices.""" + _LOGGER.info("Setup sleeping climate device %s", wrapper.name) + + ent_reg = await entity_registry.async_get_registry(hass) + entries = entity_registry.async_entries_for_config_entry( + ent_reg, config_entry.entry_id + ) + + for entry in entries: + + if entry.domain != CLIMATE_DOMAIN: + continue + + _LOGGER.debug("Found entry %s [%s]", entry.original_name, entry.domain) + async_add_entities([BlockSleepingClimate(wrapper, None, None, entry)]) + + +class BlockSleepingClimate( + RestoreEntity, + ClimateEntity, + entity.Entity, +): """Representation of a Shelly climate device.""" _attr_hvac_modes = [HVAC_MODE_OFF, HVAC_MODE_HEAT] @@ -74,45 +123,77 @@ class ShellyClimate(ShellyBlockEntity, RestoreEntity, ClimateEntity): _attr_target_temperature_step = SHTRV_01_TEMPERATURE_SETTINGS["step"] _attr_temperature_unit = TEMP_CELSIUS + # pylint: disable=super-init-not-called def __init__( - self, wrapper: BlockDeviceWrapper, sensor_block: Block, device_block: Block + self, + wrapper: BlockDeviceWrapper, + sensor_block: Block | None, + device_block: Block | None, + entry: entity_registry.RegistryEntry | None = None, ) -> None: """Initialize climate.""" - super().__init__(wrapper, sensor_block) - - self.device_block = device_block - - assert self.block.channel + self.wrapper = wrapper + self.block: Block | None = sensor_block self.control_result: dict[str, Any] | None = None + self.device_block: Block | None = device_block + self.last_state: State | None = None + self.last_state_attributes: MappingProxyType[str, Any] + self._preset_modes: list[str] = [] - self._attr_name = self.wrapper.name - self._attr_unique_id = self.wrapper.mac - self._attr_preset_modes: list[str] = [ - PRESET_NONE, - *wrapper.device.settings["thermostats"][int(self.block.channel)][ - "schedule_profile_names" - ], - ] + if self.block is not None and self.device_block is not None: + self._unique_id = f"{self.wrapper.mac}-{self.block.description}" + assert self.block.channel + self._preset_modes = [ + PRESET_NONE, + *wrapper.device.settings["thermostats"][int(self.block.channel)][ + "schedule_profile_names" + ], + ] + elif entry is not None: + self._unique_id = entry.unique_id + + @property + def unique_id(self) -> str: + """Set unique id of entity.""" + return self._unique_id + + @property + def name(self) -> str: + """Name of entity.""" + return self.wrapper.name + + @property + def should_poll(self) -> bool: + """If device should be polled.""" + return False @property def target_temperature(self) -> float | None: """Set target temperature.""" - return cast(float, self.block.targetTemp) + if self.block is not None: + return cast(float, self.block.targetTemp) + return self.last_state_attributes.get("temperature") @property def current_temperature(self) -> float | None: """Return current temperature.""" - return cast(float, self.block.temp) + if self.block is not None: + return cast(float, self.block.temp) + return self.last_state_attributes.get("current_temperature") @property def available(self) -> bool: """Device availability.""" - return not cast(bool, self.device_block.valveError) + if self.device_block is not None: + return not cast(bool, self.device_block.valveError) + return True @property def hvac_mode(self) -> str: """HVAC current mode.""" + if self.device_block is None: + return self.last_state.state if self.last_state else HVAC_MODE_OFF if self.device_block.mode is None or self._check_is_off(): return HVAC_MODE_OFF @@ -121,20 +202,45 @@ class ShellyClimate(ShellyBlockEntity, RestoreEntity, ClimateEntity): @property def preset_mode(self) -> str | None: """Preset current mode.""" + if self.device_block is None: + return self.last_state_attributes.get("preset_mode") if self.device_block.mode is None: - return None - return self._attr_preset_modes[cast(int, self.device_block.mode)] + return PRESET_NONE + return self._preset_modes[cast(int, self.device_block.mode)] @property def hvac_action(self) -> str | None: """HVAC current action.""" - if self.device_block.status is None or self._check_is_off(): + if ( + self.device_block is None + or self.device_block.status is None + or self._check_is_off() + ): return CURRENT_HVAC_OFF return ( CURRENT_HVAC_IDLE if self.device_block.status == "0" else CURRENT_HVAC_HEAT ) + @property + def preset_modes(self) -> list[str]: + """Preset available modes.""" + return self._preset_modes + + @property + def device_info(self) -> DeviceInfo: + """Device info.""" + return { + "connections": {(device_registry.CONNECTION_NETWORK_MAC, self.wrapper.mac)} + } + + @property + def channel(self) -> str | None: + """Device channel.""" + if self.block is not None: + return self.block.channel + return self.last_state_attributes.get("channel") + def _check_is_off(self) -> bool: """Return if valve is off or on.""" return bool( @@ -148,7 +254,7 @@ class ShellyClimate(ShellyBlockEntity, RestoreEntity, ClimateEntity): try: async with async_timeout.timeout(AIOSHELLY_DEVICE_TIMEOUT_SEC): return await self.wrapper.device.http_request( - "get", f"thermostat/{self.block.channel}", kwargs + "get", f"thermostat/{self.channel}", kwargs ) except (asyncio.TimeoutError, OSError) as err: _LOGGER.error( @@ -186,3 +292,41 @@ class ShellyClimate(ShellyBlockEntity, RestoreEntity, ClimateEntity): await self.set_state_full_path( schedule=1, schedule_profile=f"{preset_index}" ) + + async def async_added_to_hass(self) -> None: + """Handle entity which will be added.""" + _LOGGER.info("Restoring entity %s", self.name) + + last_state = await self.async_get_last_state() + + if last_state is not None: + self.last_state = last_state + self.last_state_attributes = self.last_state.attributes + self._preset_modes = cast( + list, self.last_state.attributes.get("preset_modes") + ) + + self.async_on_remove(self.wrapper.async_add_listener(self._update_callback)) + + async def async_update(self) -> None: + """Update entity with latest info.""" + await self.wrapper.async_request_refresh() + + @callback + def _update_callback(self) -> None: + """Handle device update.""" + if not self.wrapper.device.initialized: + self.async_write_ha_state() + return + + assert self.wrapper.device.blocks + + for block in self.wrapper.device.blocks: + if block.type == "device": + self.device_block = block + if hasattr(block, "targetTemp"): + self.block = block + + _LOGGER.debug("Entity %s attached to block", self.name) + self.async_write_ha_state() + return From cb8968887341cb0f8c636dc3286c34feb8ef76b8 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 16 Dec 2021 14:06:38 +0100 Subject: [PATCH 04/18] Fix OwnetError preventing onewire initialisation (#61696) Co-authored-by: epenet --- homeassistant/components/onewire/__init__.py | 7 ++++++- tests/components/onewire/test_init.py | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/onewire/__init__.py b/homeassistant/components/onewire/__init__.py index 753d30e5958..70a0a5fc856 100644 --- a/homeassistant/components/onewire/__init__.py +++ b/homeassistant/components/onewire/__init__.py @@ -1,6 +1,8 @@ """The 1-Wire component.""" import logging +from pyownet import protocol + from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady @@ -18,7 +20,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: onewirehub = OneWireHub(hass) try: await onewirehub.initialize(entry) - except CannotConnect as exc: + except ( + CannotConnect, # Failed to connect to the server + protocol.OwnetError, # Connected to server, but failed to list the devices + ) as exc: raise ConfigEntryNotReady() from exc hass.data[DOMAIN][entry.entry_id] = onewirehub diff --git a/tests/components/onewire/test_init.py b/tests/components/onewire/test_init.py index e3a3fdcc564..763cdc9c071 100644 --- a/tests/components/onewire/test_init.py +++ b/tests/components/onewire/test_init.py @@ -1,6 +1,8 @@ """Tests for 1-Wire config flow.""" import logging +from unittest.mock import MagicMock +from pyownet import protocol import pytest from homeassistant.components.onewire.const import DOMAIN @@ -19,6 +21,20 @@ async def test_owserver_connect_failure(hass: HomeAssistant, config_entry: Confi assert not hass.data.get(DOMAIN) +async def test_owserver_listing_failure( + hass: HomeAssistant, config_entry: ConfigEntry, owproxy: MagicMock +): + """Test listing failure raises ConfigEntryNotReady.""" + owproxy.return_value.dir.side_effect = protocol.OwnetError() + + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + assert config_entry.state is ConfigEntryState.SETUP_RETRY + assert not hass.data.get(DOMAIN) + + @pytest.mark.usefixtures("owproxy") async def test_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry): """Test being able to unload an entry.""" From 8047134c8845714e5e0655bfc6a0e68592bc1a1b Mon Sep 17 00:00:00 2001 From: Marvin Wichmann Date: Wed, 15 Dec 2021 19:30:38 +0100 Subject: [PATCH 05/18] Fix notify platform setup for KNX (#61842) * Fix notify platform setup for KNX * Apply review suggestions * Store hass config in DATA_HASS_CONFIG * Readd guard clause --- homeassistant/components/knx/__init__.py | 18 +++++++++---- homeassistant/components/knx/const.py | 3 +++ homeassistant/components/knx/notify.py | 33 ++++++++++++++---------- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/knx/__init__.py b/homeassistant/components/knx/__init__.py index 5a66824fbcb..61d49243430 100644 --- a/homeassistant/components/knx/__init__.py +++ b/homeassistant/components/knx/__init__.py @@ -29,6 +29,7 @@ from homeassistant.const import ( CONF_PORT, CONF_TYPE, EVENT_HOMEASSISTANT_STOP, + Platform, ) from homeassistant.core import Event, HomeAssistant, ServiceCall from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError @@ -44,6 +45,7 @@ from .const import ( CONF_KNX_INDIVIDUAL_ADDRESS, CONF_KNX_ROUTING, CONF_KNX_TUNNELING, + DATA_HASS_CONFIG, DATA_KNX_CONFIG, DOMAIN, KNX_ADDRESS, @@ -195,6 +197,7 @@ SERVICE_KNX_EXPOSURE_REGISTER_SCHEMA = vol.Any( async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Start the KNX integration.""" + hass.data[DATA_HASS_CONFIG] = config conf: ConfigType | None = config.get(DOMAIN) if conf is None: @@ -251,15 +254,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) hass.config_entries.async_setup_platforms( - entry, [platform for platform in SUPPORTED_PLATFORMS if platform in config] + entry, + [ + platform + for platform in SUPPORTED_PLATFORMS + if platform in config and platform is not Platform.NOTIFY + ], ) - # set up notify platform, no entry support for notify component yet, - # have to use discovery to load platform. - if NotifySchema.PLATFORM in conf: + # set up notify platform, no entry support for notify component yet + if NotifySchema.PLATFORM in config: hass.async_create_task( discovery.async_load_platform( - hass, "notify", DOMAIN, conf[NotifySchema.PLATFORM], config + hass, "notify", DOMAIN, {}, hass.data[DATA_HASS_CONFIG] ) ) @@ -312,6 +319,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: platform for platform in SUPPORTED_PLATFORMS if platform in hass.data[DATA_KNX_CONFIG] + and platform is not Platform.NOTIFY ], ) if unload_ok: diff --git a/homeassistant/components/knx/const.py b/homeassistant/components/knx/const.py index 950deff95c1..f50460de173 100644 --- a/homeassistant/components/knx/const.py +++ b/homeassistant/components/knx/const.py @@ -42,7 +42,10 @@ CONF_STATE_ADDRESS: Final = "state_address" CONF_SYNC_STATE: Final = "sync_state" CONF_KNX_INITIAL_CONNECTION_TYPES: Final = [CONF_KNX_TUNNELING, CONF_KNX_ROUTING] +# yaml config merged with config entry data DATA_KNX_CONFIG: Final = "knx_config" +# original hass yaml config +DATA_HASS_CONFIG: Final = "knx_hass_config" ATTR_COUNTER: Final = "counter" ATTR_SOURCE: Final = "source" diff --git a/homeassistant/components/knx/notify.py b/homeassistant/components/knx/notify.py index 61bee14e5e2..ee170f55802 100644 --- a/homeassistant/components/knx/notify.py +++ b/homeassistant/components/knx/notify.py @@ -11,7 +11,8 @@ from homeassistant.const import CONF_NAME from homeassistant.core import HomeAssistant from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from .const import DOMAIN, KNX_ADDRESS +from .const import DATA_KNX_CONFIG, DOMAIN, KNX_ADDRESS +from .schema import NotifySchema async def async_get_service( @@ -20,24 +21,28 @@ async def async_get_service( discovery_info: DiscoveryInfoType | None = None, ) -> KNXNotificationService | None: """Get the KNX notification service.""" - if not discovery_info: + if discovery_info is None: return None - platform_config: dict = discovery_info - xknx: XKNX = hass.data[DOMAIN].xknx + if platform_config := hass.data[DATA_KNX_CONFIG].get(NotifySchema.PLATFORM): + xknx: XKNX = hass.data[DOMAIN].xknx - notification_devices = [] - for device_config in platform_config: - notification_devices.append( - XknxNotification( - xknx, - name=device_config[CONF_NAME], - group_address=device_config[KNX_ADDRESS], + notification_devices = [] + for device_config in platform_config: + notification_devices.append( + XknxNotification( + xknx, + name=device_config[CONF_NAME], + group_address=device_config[KNX_ADDRESS], + ) ) + return ( + KNXNotificationService(notification_devices) + if notification_devices + else None ) - return ( - KNXNotificationService(notification_devices) if notification_devices else None - ) + + return None class KNXNotificationService(BaseNotificationService): From ec263840ba9a05f23d1a60b2b23c95af12e9798d Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Thu, 16 Dec 2021 00:53:20 +0100 Subject: [PATCH 06/18] Bump aiohue to 3.0.6 (#61974) --- homeassistant/components/hue/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/hue/manifest.json b/homeassistant/components/hue/manifest.json index f32d8edc284..7003a3a8ccf 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==3.0.5"], + "requirements": ["aiohue==3.0.6"], "ssdp": [ { "manufacturer": "Royal Philips Electronics", diff --git a/requirements_all.txt b/requirements_all.txt index c0048a7cafc..23d5a4d9111 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -186,7 +186,7 @@ aiohomekit==0.6.4 aiohttp_cors==0.7.0 # homeassistant.components.hue -aiohue==3.0.5 +aiohue==3.0.6 # homeassistant.components.imap aioimaplib==0.9.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 780a343463f..99627111554 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -131,7 +131,7 @@ aiohomekit==0.6.4 aiohttp_cors==0.7.0 # homeassistant.components.hue -aiohue==3.0.5 +aiohue==3.0.6 # homeassistant.components.apache_kafka aiokafka==0.6.0 From 95c0eeecfbe7ea10c9ffb78e65fee43096689348 Mon Sep 17 00:00:00 2001 From: Eduard van Valkenburg Date: Thu, 16 Dec 2021 22:33:03 +0100 Subject: [PATCH 07/18] Brunt dependency bump to 1.0.2 (#62014) --- homeassistant/components/brunt/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/brunt/manifest.json b/homeassistant/components/brunt/manifest.json index 1ddbbb62f56..7b9307e8ef2 100644 --- a/homeassistant/components/brunt/manifest.json +++ b/homeassistant/components/brunt/manifest.json @@ -3,7 +3,7 @@ "name": "Brunt Blind Engine", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/brunt", - "requirements": ["brunt==1.0.1"], + "requirements": ["brunt==1.0.2"], "codeowners": ["@eavanvalkenburg"], "iot_class": "cloud_polling" } diff --git a/requirements_all.txt b/requirements_all.txt index 23d5a4d9111..8880ce371d3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -440,7 +440,7 @@ brother==1.1.0 brottsplatskartan==0.0.1 # homeassistant.components.brunt -brunt==1.0.1 +brunt==1.0.2 # homeassistant.components.bsblan bsblan==0.4.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 99627111554..74240b6e5b1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -281,7 +281,7 @@ broadlink==0.18.0 brother==1.1.0 # homeassistant.components.brunt -brunt==1.0.1 +brunt==1.0.2 # homeassistant.components.bsblan bsblan==0.4.0 From e44d50e1b1762cf9200776500d1f012065de4b83 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 16 Dec 2021 05:37:48 -0600 Subject: [PATCH 08/18] Bump flux_led to 0.26.15 (#62017) --- homeassistant/components/flux_led/config_flow.py | 6 ++++++ homeassistant/components/flux_led/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/flux_led/config_flow.py b/homeassistant/components/flux_led/config_flow.py index cefecf216db..674c0b42a15 100644 --- a/homeassistant/components/flux_led/config_flow.py +++ b/homeassistant/components/flux_led/config_flow.py @@ -94,6 +94,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): firmware_date=None, model_info=None, model_description=None, + remote_access_enabled=None, + remote_access_host=None, + remote_access_port=None, ) return await self._async_handle_discovery() @@ -261,6 +264,9 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): firmware_date=None, model_info=None, model_description=bulb.model_data.description, + remote_access_enabled=None, + remote_access_host=None, + remote_access_port=None, ) diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json index 191cdef7c38..22a8aa405e4 100644 --- a/homeassistant/components/flux_led/manifest.json +++ b/homeassistant/components/flux_led/manifest.json @@ -3,7 +3,7 @@ "name": "Flux LED/MagicHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/flux_led", - "requirements": ["flux_led==0.26.7"], + "requirements": ["flux_led==0.26.15"], "quality_scale": "platinum", "codeowners": ["@icemanch"], "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index 8880ce371d3..8754825b8d5 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -658,7 +658,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.26.7 +flux_led==0.26.15 # homeassistant.components.homekit fnvhash==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 74240b6e5b1..dd320cb8222 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -399,7 +399,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.26.7 +flux_led==0.26.15 # homeassistant.components.homekit fnvhash==0.1.0 From d92ad76ed988b73789f0b38a67fd5c2812d7c489 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 16 Dec 2021 16:12:43 +0100 Subject: [PATCH 09/18] Fix none-check in template light (#62089) --- homeassistant/components/template/light.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/template/light.py b/homeassistant/components/template/light.py index 5d172489840..8c4ef1818a4 100644 --- a/homeassistant/components/template/light.py +++ b/homeassistant/components/template/light.py @@ -641,9 +641,13 @@ class LightTemplate(TemplateEntity, LightEntity): @callback def _update_color(self, render): """Update the hs_color from the template.""" + if render is None: + self._color = None + return + h_str = s_str = None if isinstance(render, str): - if render in (None, "None", ""): + if render in ("None", ""): self._color = None return h_str, s_str = map( From 78f40bd4bf6780533edca854c7c63c63518cba3c Mon Sep 17 00:00:00 2001 From: Maximilian <43999966+DeerMaximum@users.noreply.github.com> Date: Thu, 16 Dec 2021 20:39:49 +0000 Subject: [PATCH 10/18] Add missing timezone information (#62106) --- homeassistant/components/vallox/sensor.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/vallox/sensor.py b/homeassistant/components/vallox/sensor.py index 6eee46be737..220a9e9289b 100644 --- a/homeassistant/components/vallox/sensor.py +++ b/homeassistant/components/vallox/sensor.py @@ -22,6 +22,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType from homeassistant.helpers.update_coordinator import CoordinatorEntity +from homeassistant.util import dt as dt_util from . import ValloxDataUpdateCoordinator from .const import ( @@ -107,7 +108,7 @@ class ValloxFilterRemainingSensor(ValloxSensor): days_remaining_delta = timedelta(days=days_remaining) now = datetime.utcnow().replace(hour=13, minute=0, second=0, microsecond=0) - return now + days_remaining_delta + return (now + days_remaining_delta).astimezone(dt_util.UTC) class ValloxCellStateSensor(ValloxSensor): From b4af32624d368a3ed3a1ae77eb9af1408d452e9c Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Fri, 17 Dec 2021 09:16:02 +0100 Subject: [PATCH 11/18] Improve availability for Shelly Valve (#62129) --- homeassistant/components/shelly/climate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/shelly/climate.py b/homeassistant/components/shelly/climate.py index af84e881447..7a877e60109 100644 --- a/homeassistant/components/shelly/climate.py +++ b/homeassistant/components/shelly/climate.py @@ -187,7 +187,7 @@ class BlockSleepingClimate( """Device availability.""" if self.device_block is not None: return not cast(bool, self.device_block.valveError) - return True + return self.wrapper.last_update_success @property def hvac_mode(self) -> str: From 82173f477c1ccd20e81522b653b80d5af7fd428b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 17 Dec 2021 01:17:32 -0600 Subject: [PATCH 12/18] Fix Non-thread-safe operation in homekit light events (#62147) --- homeassistant/components/homekit/type_lights.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/homekit/type_lights.py b/homeassistant/components/homekit/type_lights.py index 90c55d52153..f925f0a15a4 100644 --- a/homeassistant/components/homekit/type_lights.py +++ b/homeassistant/components/homekit/type_lights.py @@ -120,10 +120,11 @@ class Light(HomeAccessory): if self._event_timer: self._event_timer() self._event_timer = async_call_later( - self.hass, CHANGE_COALESCE_TIME_WINDOW, self._send_events + self.hass, CHANGE_COALESCE_TIME_WINDOW, self._async_send_events ) - def _send_events(self, *_): + @callback + def _async_send_events(self, *_): """Process all changes at once.""" _LOGGER.debug("Coalesced _set_chars: %s", self._pending_events) char_values = self._pending_events From 19a0644b50f3e7ca1525d991de2861258b692617 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 17 Dec 2021 01:19:07 -0600 Subject: [PATCH 13/18] Fix Non-thread-safe operation in logbook (#62148) --- homeassistant/components/logbook/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 89e70f346ed..43cdbec2530 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -112,6 +112,7 @@ def log_entry(hass, name, message, domain=None, entity_id=None, context=None): hass.add_job(async_log_entry, hass, name, message, domain, entity_id, context) +@callback @bind_hass def async_log_entry(hass, name, message, domain=None, entity_id=None, context=None): """Add an entry to the logbook.""" From 9361c9ef60609c3e2dc1cacf589a515b7b6be3ba Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Fri, 17 Dec 2021 00:07:51 -0800 Subject: [PATCH 14/18] Bump google-nest-sdm to 0.4.9 (#62160) --- homeassistant/components/nest/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nest/manifest.json b/homeassistant/components/nest/manifest.json index 0c14e59babc..bca37ce41c6 100644 --- a/homeassistant/components/nest/manifest.json +++ b/homeassistant/components/nest/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["ffmpeg", "http", "media_source"], "documentation": "https://www.home-assistant.io/integrations/nest", - "requirements": ["python-nest==4.1.0", "google-nest-sdm==0.4.8"], + "requirements": ["python-nest==4.1.0", "google-nest-sdm==0.4.9"], "codeowners": ["@allenporter"], "quality_scale": "platinum", "dhcp": [ diff --git a/requirements_all.txt b/requirements_all.txt index 8754825b8d5..151881e5613 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -738,7 +738,7 @@ google-cloud-pubsub==2.1.0 google-cloud-texttospeech==0.4.0 # homeassistant.components.nest -google-nest-sdm==0.4.8 +google-nest-sdm==0.4.9 # homeassistant.components.google_travel_time googlemaps==2.5.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index dd320cb8222..c59e534e52d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -461,7 +461,7 @@ google-api-python-client==1.6.4 google-cloud-pubsub==2.1.0 # homeassistant.components.nest -google-nest-sdm==0.4.8 +google-nest-sdm==0.4.9 # homeassistant.components.google_travel_time googlemaps==2.5.1 From 614529d7c327609ee8121721a3995ba4825f7853 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Fri, 17 Dec 2021 10:50:10 +0100 Subject: [PATCH 15/18] Add guard in call to activate_scene in Hue (#62177) --- homeassistant/components/hue/services.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/hue/services.py b/homeassistant/components/hue/services.py index 72e88f0d956..6e68bbffbb8 100644 --- a/homeassistant/components/hue/services.py +++ b/homeassistant/components/hue/services.py @@ -146,8 +146,10 @@ async def hue_activate_scene_v2( continue # found match! if transition: - transition = transition * 100 # in steps of 100ms - await api.scenes.recall(scene.id, dynamic=dynamic, duration=transition) + transition = transition * 1000 # transition is in ms + await bridge.async_request_call( + api.scenes.recall, scene.id, dynamic=dynamic, duration=transition + ) return True LOGGER.debug( "Unable to find scene %s for group %s on bridge %s", From c445e93d45906a9c4dc279ce31340516ec7c42cd Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 17 Dec 2021 11:41:54 +0100 Subject: [PATCH 16/18] Fix threading error in scripts with repeat or choose actions (#62168) --- homeassistant/helpers/script.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/helpers/script.py b/homeassistant/helpers/script.py index d4d37e1b4ac..20a1dbb8aec 100644 --- a/homeassistant/helpers/script.py +++ b/homeassistant/helpers/script.py @@ -1052,6 +1052,7 @@ class Script: if self._change_listener_job: self._hass.async_run_hass_job(self._change_listener_job) + @callback def _chain_change_listener(self, sub_script: Script) -> None: if sub_script.is_running: self.last_action = sub_script.last_action From 54d7380f4d10c65b75489319929fbca608d029ca Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Fri, 17 Dec 2021 11:42:15 +0100 Subject: [PATCH 17/18] Fix threading error in zha (#62170) --- homeassistant/components/zha/core/discovery.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/zha/core/discovery.py b/homeassistant/components/zha/core/discovery.py index 1d9edb82980..780d7bc384b 100644 --- a/homeassistant/components/zha/core/discovery.py +++ b/homeassistant/components/zha/core/discovery.py @@ -234,6 +234,7 @@ class GroupProbe: unsub() self._unsubs.remove(unsub) + @callback def _reprobe_group(self, group_id: int) -> None: """Reprobe a group for entities after its members change.""" zha_gateway = self._hass.data[zha_const.DATA_ZHA][zha_const.DATA_ZHA_GATEWAY] From 5196a770cc4b257e28bec4749c3d104f0a1731f1 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Fri, 17 Dec 2021 11:43:38 +0100 Subject: [PATCH 18/18] Bumped version to 2021.12.3 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 6fd2760d618..81734046d2b 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -7,7 +7,7 @@ from homeassistant.backports.enum import StrEnum MAJOR_VERSION: Final = 2021 MINOR_VERSION: Final = 12 -PATCH_VERSION: Final = "2" +PATCH_VERSION: Final = "3" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0)