From 7d2fd4bce5bb3886b7acdfeff2d44533211b8800 Mon Sep 17 00:00:00 2001 From: Sjack-Sch <60797694+Sjack-Sch@users.noreply.github.com> Date: Fri, 15 Jan 2021 19:40:29 +0100 Subject: [PATCH 1/5] Fix Home Connect ambient color (#45038) --- homeassistant/components/home_connect/light.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/home_connect/light.py b/homeassistant/components/home_connect/light.py index a8d0d7ffbd3..56b0dd39fe5 100644 --- a/homeassistant/components/home_connect/light.py +++ b/homeassistant/components/home_connect/light.py @@ -104,7 +104,7 @@ class HomeConnectLight(HomeConnectEntity, LightEntity): if ATTR_BRIGHTNESS in kwargs: brightness = 10 + ceil(kwargs[ATTR_BRIGHTNESS] / 255 * 90) - hs_color = kwargs.get(ATTR_HS_COLOR, default=self._hs_color) + hs_color = kwargs.get(ATTR_HS_COLOR, self._hs_color) if hs_color is not None: rgb = color_util.color_hsv_to_RGB(*hs_color, brightness) From 2d4576ddcf543793ce4fc61b70a3cc9dfbd5e8fb Mon Sep 17 00:00:00 2001 From: Jacob Southard Date: Wed, 13 Jan 2021 08:21:32 -0600 Subject: [PATCH 2/5] Fix HomeKit climate integration for devices with a single set point in Heat_Cool mode. (#45065) * Check supported flags in auto mode, and add tests. * Fix test description. --- .../components/homekit_controller/climate.py | 53 +++++++--- .../homekit_controller/test_climate.py | 100 ++++++++++++++++++ 2 files changed, 139 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/homekit_controller/climate.py b/homeassistant/components/homekit_controller/climate.py index 042bc4771c1..cb0feb6ba77 100644 --- a/homeassistant/components/homekit_controller/climate.py +++ b/homeassistant/components/homekit_controller/climate.py @@ -346,7 +346,9 @@ class HomeKitClimateEntity(HomeKitEntity, ClimateEntity): heat_temp = kwargs.get(ATTR_TARGET_TEMP_LOW) cool_temp = kwargs.get(ATTR_TARGET_TEMP_HIGH) value = self.service.value(CharacteristicsTypes.HEATING_COOLING_TARGET) - if MODE_HOMEKIT_TO_HASS.get(value) in {HVAC_MODE_HEAT_COOL}: + if (MODE_HOMEKIT_TO_HASS.get(value) in {HVAC_MODE_HEAT_COOL}) and ( + SUPPORT_TARGET_TEMPERATURE_RANGE & self.supported_features + ): if temp is None: temp = (cool_temp + heat_temp) / 2 await self.async_put_characteristics( @@ -386,37 +388,54 @@ class HomeKitClimateEntity(HomeKitEntity, ClimateEntity): def target_temperature(self): """Return the temperature we try to reach.""" value = self.service.value(CharacteristicsTypes.HEATING_COOLING_TARGET) - if MODE_HOMEKIT_TO_HASS.get(value) not in {HVAC_MODE_HEAT, HVAC_MODE_COOL}: - return None - return self.service.value(CharacteristicsTypes.TEMPERATURE_TARGET) + if (MODE_HOMEKIT_TO_HASS.get(value) in {HVAC_MODE_HEAT, HVAC_MODE_COOL}) or ( + (MODE_HOMEKIT_TO_HASS.get(value) in {HVAC_MODE_HEAT_COOL}) + and not (SUPPORT_TARGET_TEMPERATURE_RANGE & self.supported_features) + ): + return self.service.value(CharacteristicsTypes.TEMPERATURE_TARGET) + return None @property def target_temperature_high(self): """Return the highbound target temperature we try to reach.""" value = self.service.value(CharacteristicsTypes.HEATING_COOLING_TARGET) - if MODE_HOMEKIT_TO_HASS.get(value) not in {HVAC_MODE_HEAT_COOL}: - return None - return self.service.value(CharacteristicsTypes.TEMPERATURE_COOLING_THRESHOLD) + if (MODE_HOMEKIT_TO_HASS.get(value) in {HVAC_MODE_HEAT_COOL}) and ( + SUPPORT_TARGET_TEMPERATURE_RANGE & self.supported_features + ): + return self.service.value( + CharacteristicsTypes.TEMPERATURE_COOLING_THRESHOLD + ) + return None @property def target_temperature_low(self): """Return the lowbound target temperature we try to reach.""" value = self.service.value(CharacteristicsTypes.HEATING_COOLING_TARGET) - if MODE_HOMEKIT_TO_HASS.get(value) not in {HVAC_MODE_HEAT_COOL}: - return None - return self.service.value(CharacteristicsTypes.TEMPERATURE_HEATING_THRESHOLD) + if (MODE_HOMEKIT_TO_HASS.get(value) in {HVAC_MODE_HEAT_COOL}) and ( + SUPPORT_TARGET_TEMPERATURE_RANGE & self.supported_features + ): + return self.service.value( + CharacteristicsTypes.TEMPERATURE_HEATING_THRESHOLD + ) + return None @property def min_temp(self): """Return the minimum target temp.""" value = self.service.value(CharacteristicsTypes.HEATING_COOLING_TARGET) - if MODE_HOMEKIT_TO_HASS.get(value) in {HVAC_MODE_HEAT_COOL}: + if (MODE_HOMEKIT_TO_HASS.get(value) in {HVAC_MODE_HEAT_COOL}) and ( + SUPPORT_TARGET_TEMPERATURE_RANGE & self.supported_features + ): min_temp = self.service[ CharacteristicsTypes.TEMPERATURE_HEATING_THRESHOLD ].minValue if min_temp is not None: return min_temp - if MODE_HOMEKIT_TO_HASS.get(value) in {HVAC_MODE_HEAT, HVAC_MODE_COOL}: + elif MODE_HOMEKIT_TO_HASS.get(value) in { + HVAC_MODE_HEAT, + HVAC_MODE_COOL, + HVAC_MODE_HEAT_COOL, + }: min_temp = self.service[CharacteristicsTypes.TEMPERATURE_TARGET].minValue if min_temp is not None: return min_temp @@ -426,13 +445,19 @@ class HomeKitClimateEntity(HomeKitEntity, ClimateEntity): def max_temp(self): """Return the maximum target temp.""" value = self.service.value(CharacteristicsTypes.HEATING_COOLING_TARGET) - if MODE_HOMEKIT_TO_HASS.get(value) in {HVAC_MODE_HEAT_COOL}: + if (MODE_HOMEKIT_TO_HASS.get(value) in {HVAC_MODE_HEAT_COOL}) and ( + SUPPORT_TARGET_TEMPERATURE_RANGE & self.supported_features + ): max_temp = self.service[ CharacteristicsTypes.TEMPERATURE_COOLING_THRESHOLD ].maxValue if max_temp is not None: return max_temp - if MODE_HOMEKIT_TO_HASS.get(value) in {HVAC_MODE_HEAT, HVAC_MODE_COOL}: + elif MODE_HOMEKIT_TO_HASS.get(value) in { + HVAC_MODE_HEAT, + HVAC_MODE_COOL, + HVAC_MODE_HEAT_COOL, + }: max_temp = self.service[CharacteristicsTypes.TEMPERATURE_TARGET].maxValue if max_temp is not None: return max_temp diff --git a/tests/components/homekit_controller/test_climate.py b/tests/components/homekit_controller/test_climate.py index d3f852d7a49..52671703cca 100644 --- a/tests/components/homekit_controller/test_climate.py +++ b/tests/components/homekit_controller/test_climate.py @@ -283,6 +283,106 @@ async def test_climate_cannot_set_thermostat_temp_range_in_wrong_mode(hass, utcn assert helper.characteristics[THERMOSTAT_TEMPERATURE_COOLING_THRESHOLD].value == 0 +def create_thermostat_single_set_point_auto(accessory): + """Define thermostat characteristics with a single set point in auto.""" + service = accessory.add_service(ServicesTypes.THERMOSTAT) + + char = service.add_char(CharacteristicsTypes.HEATING_COOLING_TARGET) + char.value = 0 + + char = service.add_char(CharacteristicsTypes.HEATING_COOLING_CURRENT) + char.value = 0 + + char = service.add_char(CharacteristicsTypes.TEMPERATURE_TARGET) + char.minValue = 7 + char.maxValue = 35 + char.value = 0 + + char = service.add_char(CharacteristicsTypes.TEMPERATURE_CURRENT) + char.value = 0 + + char = service.add_char(CharacteristicsTypes.RELATIVE_HUMIDITY_TARGET) + char.value = 0 + + char = service.add_char(CharacteristicsTypes.RELATIVE_HUMIDITY_CURRENT) + char.value = 0 + + +async def test_climate_check_min_max_values_per_mode_sspa_device(hass, utcnow): + """Test appropriate min/max values for each mode on sspa devices.""" + helper = await setup_test_component(hass, create_thermostat_single_set_point_auto) + + await hass.services.async_call( + DOMAIN, + SERVICE_SET_HVAC_MODE, + {"entity_id": "climate.testdevice", "hvac_mode": HVAC_MODE_HEAT}, + blocking=True, + ) + climate_state = await helper.poll_and_get_state() + assert climate_state.attributes["min_temp"] == 7 + assert climate_state.attributes["max_temp"] == 35 + + await hass.services.async_call( + DOMAIN, + SERVICE_SET_HVAC_MODE, + {"entity_id": "climate.testdevice", "hvac_mode": HVAC_MODE_COOL}, + blocking=True, + ) + climate_state = await helper.poll_and_get_state() + assert climate_state.attributes["min_temp"] == 7 + assert climate_state.attributes["max_temp"] == 35 + + await hass.services.async_call( + DOMAIN, + SERVICE_SET_HVAC_MODE, + {"entity_id": "climate.testdevice", "hvac_mode": HVAC_MODE_HEAT_COOL}, + blocking=True, + ) + climate_state = await helper.poll_and_get_state() + assert climate_state.attributes["min_temp"] == 7 + assert climate_state.attributes["max_temp"] == 35 + + +async def test_climate_set_thermostat_temp_on_sspa_device(hass, utcnow): + """Test setting temperature in different modes on device with single set point in auto.""" + helper = await setup_test_component(hass, create_thermostat_single_set_point_auto) + + await hass.services.async_call( + DOMAIN, + SERVICE_SET_HVAC_MODE, + {"entity_id": "climate.testdevice", "hvac_mode": HVAC_MODE_HEAT}, + blocking=True, + ) + + await hass.services.async_call( + DOMAIN, + SERVICE_SET_TEMPERATURE, + {"entity_id": "climate.testdevice", "temperature": 21}, + blocking=True, + ) + assert helper.characteristics[TEMPERATURE_TARGET].value == 21 + + await hass.services.async_call( + DOMAIN, + SERVICE_SET_HVAC_MODE, + {"entity_id": "climate.testdevice", "hvac_mode": HVAC_MODE_HEAT_COOL}, + blocking=True, + ) + assert helper.characteristics[TEMPERATURE_TARGET].value == 21 + + await hass.services.async_call( + DOMAIN, + SERVICE_SET_TEMPERATURE, + { + "entity_id": "climate.testdevice", + "hvac_mode": HVAC_MODE_HEAT_COOL, + "temperature": 22, + }, + blocking=True, + ) + assert helper.characteristics[TEMPERATURE_TARGET].value == 22 + + async def test_climate_change_thermostat_humidity(hass, utcnow): """Test that we can turn a HomeKit thermostat on and off again.""" helper = await setup_test_component(hass, create_thermostat_service) From a66528c64031c4ec8e2e497391f15389df888fce Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Fri, 15 Jan 2021 23:32:38 +0100 Subject: [PATCH 3/5] Fix all forecast datetime values in OpenWeatherMap (#45202) --- homeassistant/components/openweathermap/sensor.py | 10 +--------- .../openweathermap/weather_update_coordinator.py | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/openweathermap/sensor.py b/homeassistant/components/openweathermap/sensor.py index b1ba4ab7625..39c50c3b941 100644 --- a/homeassistant/components/openweathermap/sensor.py +++ b/homeassistant/components/openweathermap/sensor.py @@ -1,10 +1,7 @@ """Support for the OpenWeatherMap (OWM) service.""" -import datetime - from .abstract_owm_sensor import AbstractOpenWeatherMapSensor from .const import ( ATTR_API_FORECAST, - DEVICE_CLASS_TIMESTAMP, DOMAIN, ENTRY_NAME, ENTRY_WEATHER_COORDINATOR, @@ -98,10 +95,5 @@ class OpenWeatherMapForecastSensor(AbstractOpenWeatherMapSensor): """Return the state of the device.""" forecasts = self._weather_coordinator.data.get(ATTR_API_FORECAST) if forecasts is not None and len(forecasts) > 0: - value = forecasts[0].get(self._sensor_type, None) - if self._device_class is DEVICE_CLASS_TIMESTAMP: - value = datetime.datetime.fromtimestamp( - value, datetime.timezone.utc - ).isoformat() - return value + return forecasts[0].get(self._sensor_type, None) return None diff --git a/homeassistant/components/openweathermap/weather_update_coordinator.py b/homeassistant/components/openweathermap/weather_update_coordinator.py index 605e6f9edc1..7b127080269 100644 --- a/homeassistant/components/openweathermap/weather_update_coordinator.py +++ b/homeassistant/components/openweathermap/weather_update_coordinator.py @@ -138,7 +138,7 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator): def _convert_forecast(self, entry): forecast = { - ATTR_FORECAST_TIME: entry.reference_time("unix"), + ATTR_FORECAST_TIME: dt.utc_from_timestamp(entry.reference_time("unix")), ATTR_FORECAST_PRECIPITATION: self._calc_precipitation( entry.rain, entry.snow ), From 120304f23533bf6416d515f06adbc68823d904fc Mon Sep 17 00:00:00 2001 From: Alexei Chetroi Date: Sat, 16 Jan 2021 13:43:35 -0500 Subject: [PATCH 4/5] Bump up ZHA dependency (#45230) --- homeassistant/components/zha/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index 54fceda03a6..527b218e1f3 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -10,7 +10,7 @@ "zha-quirks==0.0.51", "zigpy-cc==0.5.2", "zigpy-deconz==0.11.1", - "zigpy==0.29.0", + "zigpy==0.30.0", "zigpy-xbee==0.13.0", "zigpy-zigate==0.7.3", "zigpy-znp==0.3.0" diff --git a/requirements_all.txt b/requirements_all.txt index eaca17a952e..f95bcd194aa 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2363,7 +2363,7 @@ zigpy-zigate==0.7.3 zigpy-znp==0.3.0 # homeassistant.components.zha -zigpy==0.29.0 +zigpy==0.30.0 # homeassistant.components.zoneminder zm-py==0.5.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a8d21dbaa42..0d737ccc77d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1162,4 +1162,4 @@ zigpy-zigate==0.7.3 zigpy-znp==0.3.0 # homeassistant.components.zha -zigpy==0.29.0 +zigpy==0.30.0 From 34b90fd92516da7024de08dbb9dae26b37db59f5 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 16 Jan 2021 22:05:42 +0100 Subject: [PATCH 5/5] Bumped version to 2021.1.4 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 318166c0f7d..cda54d78b21 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 2021 MINOR_VERSION = 1 -PATCH_VERSION = "3" +PATCH_VERSION = "4" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 1)