From dc71b7421c075283703f7a40badc3ceef8cd282d Mon Sep 17 00:00:00 2001
From: Robert Svensson <Kane610@users.noreply.github.com>
Date: Tue, 13 Oct 2020 22:11:09 +0200
Subject: [PATCH] Improve deCONZ climate platform handling unsupported commands
 (#41780)

* Improve deCONZ climate platform

* Raise valueerror if missing input

* Fix linting
---
 homeassistant/components/deconz/climate.py | 34 ++++++++++------------
 tests/components/deconz/test_climate.py    | 30 +++++++++++++++++++
 2 files changed, 46 insertions(+), 18 deletions(-)

diff --git a/homeassistant/components/deconz/climate.py b/homeassistant/components/deconz/climate.py
index e4de5badb61..e79ed1dfb0c 100644
--- a/homeassistant/components/deconz/climate.py
+++ b/homeassistant/components/deconz/climate.py
@@ -16,7 +16,7 @@ from .const import ATTR_OFFSET, ATTR_VALVE, NEW_SENSOR
 from .deconz_device import DeconzDevice
 from .gateway import get_gateway_from_config_entry
 
-SUPPORT_HVAC = [HVAC_MODE_AUTO, HVAC_MODE_HEAT, HVAC_MODE_OFF]
+HVAC_MODES = {HVAC_MODE_AUTO: "auto", HVAC_MODE_HEAT: "heat", HVAC_MODE_OFF: "off"}
 
 
 async def async_setup_entry(hass, config_entry, async_add_entities):
@@ -72,19 +72,19 @@ class DeconzThermostat(DeconzDevice, ClimateEntity):
 
         Need to be one of HVAC_MODE_*.
         """
-        if self._device.mode in SUPPORT_HVAC:
-            return self._device.mode
+        for hass_hvac_mode, device_mode in HVAC_MODES.items():
+            if self._device.mode == device_mode:
+                return hass_hvac_mode
+
         if self._device.state_on:
             return HVAC_MODE_HEAT
+
         return HVAC_MODE_OFF
 
     @property
-    def hvac_modes(self):
-        """Return the list of available hvac operation modes.
-
-        Need to be a subset of HVAC_MODES.
-        """
-        return SUPPORT_HVAC
+    def hvac_modes(self) -> list:
+        """Return the list of available hvac operation modes."""
+        return list(HVAC_MODES)
 
     @property
     def current_temperature(self):
@@ -98,21 +98,19 @@ class DeconzThermostat(DeconzDevice, ClimateEntity):
 
     async def async_set_temperature(self, **kwargs):
         """Set new target temperature."""
-        data = {}
+        if ATTR_TEMPERATURE not in kwargs:
+            raise ValueError(f"Expected attribute {ATTR_TEMPERATURE}")
 
-        if ATTR_TEMPERATURE in kwargs:
-            data["heatsetpoint"] = kwargs[ATTR_TEMPERATURE] * 100
+        data = {"heatsetpoint": kwargs[ATTR_TEMPERATURE] * 100}
 
         await self._device.async_set_config(data)
 
     async def async_set_hvac_mode(self, hvac_mode):
         """Set new target hvac mode."""
-        if hvac_mode == HVAC_MODE_AUTO:
-            data = {"mode": "auto"}
-        elif hvac_mode == HVAC_MODE_HEAT:
-            data = {"mode": "heat"}
-        elif hvac_mode == HVAC_MODE_OFF:
-            data = {"mode": "off"}
+        if hvac_mode not in HVAC_MODES:
+            raise ValueError(f"Unsupported mode {hvac_mode}")
+
+        data = {"mode": HVAC_MODES[hvac_mode]}
 
         await self._device.async_set_config(data)
 
diff --git a/tests/components/deconz/test_climate.py b/tests/components/deconz/test_climate.py
index 3eb893d8a6e..7e49dd3c907 100644
--- a/tests/components/deconz/test_climate.py
+++ b/tests/components/deconz/test_climate.py
@@ -1,6 +1,8 @@
 """deCONZ climate platform tests."""
 from copy import deepcopy
 
+import pytest
+
 from homeassistant.components import deconz
 import homeassistant.components.climate as climate
 from homeassistant.components.deconz.gateway import get_gateway_from_config_entry
@@ -155,6 +157,18 @@ async def test_climate_devices(hass):
             "put", "/sensors/1/config", json={"mode": "off"}
         )
 
+    # Service set HVAC mode to unsupported value
+
+    with patch.object(
+        thermostat_device, "_request", return_value=True
+    ) as set_callback, pytest.raises(ValueError):
+        await hass.services.async_call(
+            climate.DOMAIN,
+            climate.SERVICE_SET_HVAC_MODE,
+            {"entity_id": "climate.thermostat", "hvac_mode": "cool"},
+            blocking=True,
+        )
+
     # Service set temperature to 20
 
     with patch.object(thermostat_device, "_request", return_value=True) as set_callback:
@@ -168,6 +182,22 @@ async def test_climate_devices(hass):
             "put", "/sensors/1/config", json={"heatsetpoint": 2000.0}
         )
 
+    # Service set temperature without providing temperature attribute
+
+    with patch.object(
+        thermostat_device, "_request", return_value=True
+    ) as set_callback, pytest.raises(ValueError):
+        await hass.services.async_call(
+            climate.DOMAIN,
+            climate.SERVICE_SET_TEMPERATURE,
+            {
+                "entity_id": "climate.thermostat",
+                "target_temp_high": 30,
+                "target_temp_low": 10,
+            },
+            blocking=True,
+        )
+
     await hass.config_entries.async_unload(config_entry.entry_id)
 
     assert len(hass.states.async_all()) == 0
-- 
GitLab