Repository: XKNX/xknx
Pull request #66 (2017-09-22 22:54:04)

Add AUTO mode to HVAC
According to KNX specification v2.1 3/7/2 Datapoint Types, section 4.3, DPT 20.102 value 0 is "Auto"

This value is used for example when the climate control is able to use a presence detector which governs the HVAC type.

I have however no idea how the AUTO mode is coded in this strange "non-standardised DP type", maybe `if raw[0] & 15 == 0` but I don't know it for sure.


Patch:
diff --git a/xknx/devices/climate.py b/xknx/devices/climate.py
index 6588de38..b8887d8d 100644
--- a/xknx/devices/climate.py
+++ b/xknx/devices/climate.py
@@ -231,7 +231,7 @@ class Climate(Device):
         if self.group_address_operation_mode is not None:
             return list(HVACOperationMode)
         if self.group_address_controller_status is not None:
-            return list(HVACOperationMode)
+            return list( m for m in HVACOperationMode if m != HVACOperationMode.AUTO )
 
         # Operation modes only supported partially
         operation_modes = []
diff --git a/xknx/devices/cover.py b/xknx/devices/cover.py
index f5e8e382..d099b81c 100644
--- a/xknx/devices/cover.py
+++ b/xknx/devices/cover.py
@@ -122,7 +122,7 @@ class Cover(Device):
             .format(
                 self.name,
                 self.updown.group_addr_str(),
-                self.step.group_addr_str(),
+                self.step.group_address(),
                 self.position.group_addr_str(),
                 self.angle.group_addr_str(),
                 self.travel_time_down,
diff --git a/xknx/knx/dpt_hvac_mode.py b/xknx/knx/dpt_hvac_mode.py
index 561859ad..2d80c85c 100644
--- a/xknx/knx/dpt_hvac_mode.py
+++ b/xknx/knx/dpt_hvac_mode.py
@@ -7,6 +7,7 @@ from .dpt import DPTBase
 class HVACOperationMode(Enum):
     """Enum for the different KNX HVAC operation modes."""
 
+    AUTO    = "Auto"
     COMFORT = "Comfort"
     STANDBY = "Standby"
     NIGHT = "Night"
@@ -32,11 +33,15 @@ class DPTHVACMode(DPTBase):
             return HVACOperationMode.STANDBY
         elif raw[0] == 0x01:
             return HVACOperationMode.COMFORT
+        elif raw[0] == 0x00:
+            return HVACOperationMode.AUTO
 
     @classmethod
     def to_knx(cls, value):
         """Serialize to KNX/IP raw data."""
-        if value == HVACOperationMode.COMFORT:
+        if value == HVACOperationMode.AUTO:
+            return (0,)
+        elif value == HVACOperationMode.COMFORT:
             return (1,)
         elif value == HVACOperationMode.STANDBY:
             return (2,)
@@ -73,7 +78,10 @@ class DPTControllerStatus(DPTBase):
     @classmethod
     def to_knx(cls, value):
         """Serialize to KNX/IP raw data."""
-        if value == HVACOperationMode.COMFORT:
+        if value == HVACOperationMode.AUTO:
+            from xknx.exceptions import ConversionError
+            raise ConversionError(value)
+        elif value == HVACOperationMode.COMFORT:
             return (0x21,)
         elif value == HVACOperationMode.STANDBY:
             return (0x22,)


Test patch:
diff --git a/test/climate_test.py b/test/climate_test.py
index 663cf670..019f60ca 100644
--- a/test/climate_test.py
+++ b/test/climate_test.py
@@ -289,6 +289,7 @@ class TestClimate(unittest.TestCase):
             group_address_controller_status='1/2/4')
 
         for operation_mode in HVACOperationMode:
+            if operation_mode == HVACOperationMode.AUTO: continue
             self.loop.run_until_complete(asyncio.Task(climate.set_operation_mode(operation_mode)))
             self.assertEqual(xknx.telegrams.qsize(), 1)
             telegram = xknx.telegrams.get_nowait()
@@ -444,6 +445,7 @@ class TestClimate(unittest.TestCase):
             self.loop.run_until_complete(asyncio.Task(climate.process(telegram)))
             self.assertEqual(climate.operation_mode, operation_mode)
         for operation_mode in HVACOperationMode:
+            if operation_mode == HVACOperationMode.AUTO: continue
             telegram = Telegram(Address('1/2/3'))
             telegram.payload = DPTArray(DPTControllerStatus.to_knx(operation_mode))
             self.loop.run_until_complete(asyncio.Task(climate.process(telegram)))
@@ -508,7 +510,8 @@ class TestClimate(unittest.TestCase):
             group_address_operation_mode='1/2/5')
         self.assertEqual(
             climate.get_supported_operation_modes(),
-            [HVACOperationMode.COMFORT,
+            [HVACOperationMode.AUTO,
+             HVACOperationMode.COMFORT,
              HVACOperationMode.STANDBY,
              HVACOperationMode.NIGHT,
              HVACOperationMode.FROST_PROTECTION])
diff --git a/test/dpt_hvac_mode_test.py b/test/dpt_hvac_mode_test.py
index db46d56c..4b435d45 100644
--- a/test/dpt_hvac_mode_test.py
+++ b/test/dpt_hvac_mode_test.py
@@ -12,6 +12,7 @@ class TestDPTControllerStatus(unittest.TestCase):
 
     def test_mode_to_knx(self):
         """Test parsing DPTHVACMode to KNX."""
+        self.assertEqual(DPTHVACMode.to_knx(HVACOperationMode.AUTO), (0x00,))
         self.assertEqual(DPTHVACMode.to_knx(HVACOperationMode.COMFORT), (0x01,))
         self.assertEqual(DPTHVACMode.to_knx(HVACOperationMode.STANDBY), (0x02,))
         self.assertEqual(DPTHVACMode.to_knx(HVACOperationMode.NIGHT), (0x03,))
@@ -19,6 +20,7 @@ class TestDPTControllerStatus(unittest.TestCase):
 
     def test_mode_from_knx(self):
         """Test parsing DPTHVACMode from KNX."""
+        self.assertEqual(DPTHVACMode.from_knx((0x00,)), HVACOperationMode.AUTO)
         self.assertEqual(DPTHVACMode.from_knx((0x01,)), HVACOperationMode.COMFORT)
         self.assertEqual(DPTHVACMode.from_knx((0x02,)), HVACOperationMode.STANDBY)
         self.assertEqual(DPTHVACMode.from_knx((0x03,)), HVACOperationMode.NIGHT)
@@ -26,6 +28,8 @@ class TestDPTControllerStatus(unittest.TestCase):
 
     def test_controller_status_to_knx(self):
         """Test parsing DPTControllerStatus to KNX."""
+        with self.assertRaises(ConversionError):
+            DPTControllerStatus.to_knx(HVACOperationMode.AUTO)
         self.assertEqual(DPTControllerStatus.to_knx(HVACOperationMode.COMFORT), (0x21,))
         self.assertEqual(DPTControllerStatus.to_knx(HVACOperationMode.STANDBY), (0x22,))
         self.assertEqual(DPTControllerStatus.to_knx(HVACOperationMode.NIGHT), (0x24,))