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) 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/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 ), 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/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) 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 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)