Sfoglia il codice sorgente

Convert Kogan Flame effect heater backlight and flame to use light effects.

Complete unit tests for the heater and make them pass.
Add translations for the flame and backlight light entities.
Jason Rumney 4 anni fa
parent
commit
8a8248eb2c

+ 1 - 0
ACKNOWLEDGEMENTS.md

@@ -50,3 +50,4 @@ Further device support has been made with the assistance of users.  Please consi
  - [nickdos](https://github.com/nickdos) for assistance in supporting Stirling FS1-40DC fan.
  - [nickdos](https://github.com/nickdos) for assistance in supporting Stirling FS1-40DC fan.
  - [Skro11-ru](https://github.com/Skro11-ru) for assistance in supporting Moes BHT-002 variant without external temperature sensor.
  - [Skro11-ru](https://github.com/Skro11-ru) for assistance in supporting Moes BHT-002 variant without external temperature sensor.
  - [novisys](https://github.com/novisys) for clarifications about BHT-6000 thermostat functionality.
  - [novisys](https://github.com/novisys) for clarifications about BHT-6000 thermostat functionality.
+ - [nzcodarnoc](https://github.com/nzcodarnoc) for contributing support for Kogan KASHMFP heaters.

+ 1 - 0
README.md

@@ -39,6 +39,7 @@ the device will not work despite being listed below.
 - Eurom heater
 - Eurom heater
 - Purline Hoti M100 heater
 - Purline Hoti M100 heater
 - Wetair WCH-750 heater
 - Wetair WCH-750 heater
+- Kogan Flame effect heater - KAWHMFP20BA model
 
 
 ### Air Conditioners / Heatpumps
 ### Air Conditioners / Heatpumps
 
 

+ 4 - 6
custom_components/tuya_local/devices/kogan_kashmfp20ba_heater.yaml

@@ -34,12 +34,11 @@ primary_entity:
       readonly: true
       readonly: true
 secondary_entities:
 secondary_entities:
   - entity: light
   - entity: light
-    name: "backlight"
-    icon: "mdi:lightbulb"
+    name: "Backlight"
     dps:
     dps:
       - id: 5
       - id: 5
         type: string
         type: string
-        name: back_light
+        name: effect
         mapping:
         mapping:
           - dps_val: "white"
           - dps_val: "white"
             value: "white"
             value: "white"
@@ -54,12 +53,11 @@ secondary_entities:
           - dps_val: "blueorange"
           - dps_val: "blueorange"
             value: "blueorange"
             value: "blueorange"
   - entity: light
   - entity: light
-    name: "flame_colour"
-    icon: "mdi:lightbulb"
+    name: "Flame"
     dps:
     dps:
       - id: 6
       - id: 6
         type: string
         type: string
-        name: flame_color
+        name: effect
         mapping:
         mapping:
           - dps_val: "orange"
           - dps_val: "orange"
             value: "orange"
             value: "orange"

+ 1 - 0
custom_components/tuya_local/generic/light.py

@@ -6,6 +6,7 @@ devices, so only providing simple on/off control.
 from homeassistant.components.light import (
 from homeassistant.components.light import (
     LightEntity,
     LightEntity,
     ATTR_BRIGHTNESS,
     ATTR_BRIGHTNESS,
+    ATTR_EFFECT,
     COLOR_MODE_BRIGHTNESS,
     COLOR_MODE_BRIGHTNESS,
     COLOR_MODE_ONOFF,
     COLOR_MODE_ONOFF,
     COLOR_MODE_UNKNOWN,
     COLOR_MODE_UNKNOWN,

+ 6 - 0
custom_components/tuya_local/translations/en.json

@@ -32,10 +32,13 @@
 		    "climate_dehumidifier_as_climate": "Include a climate entity for the dehumidifier (deprecated, recommend using humidifier and fan instead)",
 		    "climate_dehumidifier_as_climate": "Include a climate entity for the dehumidifier (deprecated, recommend using humidifier and fan instead)",
 		    "fan_intensity": "Include intensity as a fan entitiy",
 		    "fan_intensity": "Include intensity as a fan entitiy",
 		    "light_aq_indicator": "Include AQ indicator as a light entity",
 		    "light_aq_indicator": "Include AQ indicator as a light entity",
+		    "light_backlight": "Include backlight as a light entity",
 		    "light_display": "Include display as a light entity",
 		    "light_display": "Include display as a light entity",
+		    "light_flame": "Include flame as a light entity",
 		    "light_uv_sterilization": "Include UV sterilization as a light entitiy",
 		    "light_uv_sterilization": "Include UV sterilization as a light entitiy",
 		    "lock_child_lock": "Include child lock as a lock entity",
 		    "lock_child_lock": "Include child lock as a lock entity",
 		    "number_timer": "Include timer as a number entity",
 		    "number_timer": "Include timer as a number entity",
+		    "select_timer": "Include timer as a select entity",
 		    "sensor_current_humidity": "Include current humidity as a sensor entity",
 		    "sensor_current_humidity": "Include current humidity as a sensor entity",
 		    "sensor_current_temperature": "Include current temperature as a sensor entity",
 		    "sensor_current_temperature": "Include current temperature as a sensor entity",
 		    "switch_air_clean": "Include air clean as a switch entity",
 		    "switch_air_clean": "Include air clean as a switch entity",
@@ -75,10 +78,13 @@
 		"climate_dehumidifier_as_climate": "Include a climate entity for the dehumidifier (deprecated, recommend using humidifier and fan instead)",
 		"climate_dehumidifier_as_climate": "Include a climate entity for the dehumidifier (deprecated, recommend using humidifier and fan instead)",
 		"fan_intensity": "Include intensity as a fan entitiy",
 		"fan_intensity": "Include intensity as a fan entitiy",
 		"light_aq_indicator": "Include AQ indicator as a light entity",
 		"light_aq_indicator": "Include AQ indicator as a light entity",
+		"light_backlight": "Include backlight as a light entity",
 		"light_display": "Include display as a light entity",
 		"light_display": "Include display as a light entity",
+		"light_flame": "Include flame as a light entity",
 		"light_uv_sterilization": "Include UV sterilization as a light entitiy",
 		"light_uv_sterilization": "Include UV sterilization as a light entitiy",
 		"lock_child_lock": "Include child lock as a lock entity",
 		"lock_child_lock": "Include child lock as a lock entity",
 		"number_timer": "Include timer as a number entity",
 		"number_timer": "Include timer as a number entity",
+		"select_timer": "Include timer as a select entity",
 		"sensor_current_humidity": "Include current humidity as a sensor entity",
 		"sensor_current_humidity": "Include current humidity as a sensor entity",
 		"sensor_current_temperature": "Include current temperature as a sensor entity",
 		"sensor_current_temperature": "Include current temperature as a sensor entity",
 		"switch_air_clean": "Include air clean as a switch entity",
 		"switch_air_clean": "Include air clean as a switch entity",

+ 3 - 0
tests/devices/base_device_tests.py

@@ -164,6 +164,9 @@ class BasicLightTests:
         self.basicLight = subject
         self.basicLight = subject
         self.basicLightDps = dps
         self.basicLightDps = dps
 
 
+    def test_basic_light_supported_features(self):
+        self.assertEqual(self.basicLight.supported_features, 0)
+
     def test_basic_light_supported_color_modes(self):
     def test_basic_light_supported_color_modes(self):
         self.assertCountEqual(
         self.assertCountEqual(
             self.basicLight.supported_color_modes,
             self.basicLight.supported_color_modes,

+ 102 - 27
tests/devices/test_kogan_kashmfp20ba_heater.py

@@ -4,19 +4,25 @@ from homeassistant.components.climate.const import (
     SUPPORT_PRESET_MODE,
     SUPPORT_PRESET_MODE,
     SUPPORT_TARGET_TEMPERATURE,
     SUPPORT_TARGET_TEMPERATURE,
 )
 )
-from homeassistant.components.lock import STATE_LOCKED, STATE_UNLOCKED
+from homeassistant.components.light import (
+    COLOR_MODE_UNKNOWN,
+    SUPPORT_EFFECT,
+)
 from homeassistant.const import STATE_UNAVAILABLE
 from homeassistant.const import STATE_UNAVAILABLE
 
 
 from ..const import KOGAN_KASHMFP20BA_HEATER_PAYLOAD
 from ..const import KOGAN_KASHMFP20BA_HEATER_PAYLOAD
 from ..helpers import assert_device_properties_set
 from ..helpers import assert_device_properties_set
 from .base_device_tests import TuyaDeviceTestCase
 from .base_device_tests import TuyaDeviceTestCase
 
 
-HVACMODE_DPS = "2"
+HVACMODE_DPS = "1"
+PRESET_DPS = "2"
 TEMPERATURE_DPS = "3"
 TEMPERATURE_DPS = "3"
 CURRENTTEMP_DPS = "4"
 CURRENTTEMP_DPS = "4"
+BACKLIGHT_DPS = "5"
+FLAME_DPS = "6"
 
 
 
 
-class TestGoldairKoganKAHTPHeater(TuyaDeviceTestCase):
+class TestKoganKASHMF20BAHeater(TuyaDeviceTestCase):
     __test__ = True
     __test__ = True
 
 
     def setUp(self):
     def setUp(self):
@@ -24,11 +30,13 @@ class TestGoldairKoganKAHTPHeater(TuyaDeviceTestCase):
             "kogan_kashmfp20ba_heater.yaml", KOGAN_KASHMFP20BA_HEATER_PAYLOAD
             "kogan_kashmfp20ba_heater.yaml", KOGAN_KASHMFP20BA_HEATER_PAYLOAD
         )
         )
         self.subject = self.entities.get("climate")
         self.subject = self.entities.get("climate")
+        self.backlight = self.entities.get("light_backlight")
+        self.flame = self.entities.get("light_flame")
 
 
     def test_supported_features(self):
     def test_supported_features(self):
         self.assertEqual(
         self.assertEqual(
             self.subject.supported_features,
             self.subject.supported_features,
-            SUPPORT_TARGET_TEMPERATURE,
+            SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE,
         )
         )
 
 
     def test_icon(self):
     def test_icon(self):
@@ -51,10 +59,10 @@ class TestGoldairKoganKAHTPHeater(TuyaDeviceTestCase):
         self.assertEqual(self.subject.target_temperature_step, 1)
         self.assertEqual(self.subject.target_temperature_step, 1)
 
 
     def test_minimum_target_temperature(self):
     def test_minimum_target_temperature(self):
-        self.assertEqual(self.subject.min_temp, 5)
+        self.assertEqual(self.subject.min_temp, 10)
 
 
     def test_maximum_target_temperature(self):
     def test_maximum_target_temperature(self):
-        self.assertEqual(self.subject.max_temp, 40)
+        self.assertEqual(self.subject.max_temp, 30)
 
 
     async def test_legacy_set_temperature_with_temperature(self):
     async def test_legacy_set_temperature_with_temperature(self):
         async with assert_device_properties_set(
         async with assert_device_properties_set(
@@ -93,14 +101,14 @@ class TestGoldairKoganKAHTPHeater(TuyaDeviceTestCase):
 
 
     async def test_set_target_temperature_fails_outside_valid_range(self):
     async def test_set_target_temperature_fails_outside_valid_range(self):
         with self.assertRaisesRegex(
         with self.assertRaisesRegex(
-            ValueError, "temperature \\(4\\) must be between 5 and 40"
+            ValueError, "temperature \\(9\\) must be between 10 and 30"
         ):
         ):
-            await self.subject.async_set_target_temperature(4)
+            await self.subject.async_set_target_temperature(9)
 
 
         with self.assertRaisesRegex(
         with self.assertRaisesRegex(
-            ValueError, "temperature \\(41\\) must be between 5 and 40"
+            ValueError, "temperature \\(31\\) must be between 10 and 30"
         ):
         ):
-            await self.subject.async_set_target_temperature(41)
+            await self.subject.async_set_target_temperature(31)
 
 
     def test_current_temperature(self):
     def test_current_temperature(self):
         self.dps[CURRENTTEMP_DPS] = 25
         self.dps[CURRENTTEMP_DPS] = 25
@@ -132,38 +140,105 @@ class TestGoldairKoganKAHTPHeater(TuyaDeviceTestCase):
             await self.subject.async_set_hvac_mode(HVAC_MODE_OFF)
             await self.subject.async_set_hvac_mode(HVAC_MODE_OFF)
 
 
     def test_preset_mode(self):
     def test_preset_mode(self):
-        self.dps[PRESET_DPS] = "Off"
-        self.assertEqual(self.subject.preset_mode, "Off")
-
-        self.dps[PRESET_DPS] = "Low"
-        self.assertEqual(self.subject.preset_mode, "Low")
+        self.dps[PRESET_DPS] = "low"
+        self.assertEqual(self.subject.preset_mode, "low")
 
 
-        self.dps[PRESET_DPS] = "High"
-        self.assertEqual(self.subject.preset_mode, "High")
+        self.dps[PRESET_DPS] = "high"
+        self.assertEqual(self.subject.preset_mode, "high")
 
 
         self.dps[PRESET_DPS] = None
         self.dps[PRESET_DPS] = None
         self.assertIs(self.subject.preset_mode, None)
         self.assertIs(self.subject.preset_mode, None)
 
 
     def test_preset_modes(self):
     def test_preset_modes(self):
-        self.assertCountEqual(self.subject.preset_modes, ["Off", "Low", "High"])
+        self.assertCountEqual(self.subject.preset_modes, ["low", "high"])
 
 
     async def test_set_preset_mode_to_low(self):
     async def test_set_preset_mode_to_low(self):
         async with assert_device_properties_set(
         async with assert_device_properties_set(
             self.subject._device,
             self.subject._device,
-            {PRESET_DPS: "Off"},
+            {PRESET_DPS: "low"},
         ):
         ):
-            await self.subject.async_set_preset_mode("Off")
+            await self.subject.async_set_preset_mode("low")
 
 
-    async def test_set_preset_mode_to_low(self):
+    async def test_set_preset_mode_to_high(self):
         async with assert_device_properties_set(
         async with assert_device_properties_set(
             self.subject._device,
             self.subject._device,
-            {PRESET_DPS: "Low"},
+            {PRESET_DPS: "high"},
         ):
         ):
-            await self.subject.async_set_preset_mode("Low")
+            await self.subject.async_set_preset_mode("high")
+
+    def test_device_state_attribures(self):
+        self.assertEqual(self.subject.device_state_attributes, {})
+        self.assertEqual(self.backlight.device_state_attributes, {})
+        self.assertEqual(self.flame.device_state_attributes, {})
+
+    def test_lighting_supported_color_modes(self):
+        self.assertCountEqual(self.backlight.supported_color_modes, [])
+        self.assertCountEqual(self.flame.supported_color_modes, [])
+
+    def test_lighting_supported_features(self):
+        self.assertEqual(self.backlight.supported_features, SUPPORT_EFFECT)
+        self.assertEqual(self.flame.supported_features, SUPPORT_EFFECT)
+
+    def test_lighting_color_mode(self):
+        self.assertEqual(self.backlight.color_mode, COLOR_MODE_UNKNOWN)
+        self.assertEqual(self.flame.color_mode, COLOR_MODE_UNKNOWN)
+
+    def test_lighting_is_on(self):
+        self.assertTrue(self.backlight.is_on)
+        self.assertTrue(self.flame.is_on)
+
+    def test_lighting_brightness(self):
+        self.assertIsNone(self.backlight.brightness)
+        self.assertIsNone(self.flame.brightness)
+
+    def test_backlight_effect_list(self):
+        self.assertCountEqual(
+            self.backlight.effect_list,
+            [
+                "white",
+                "blue",
+                "orange",
+                "whiteblue",
+                "whiteorange",
+                "blueorange",
+            ],
+        )
 
 
-    async def test_set_preset_mode_to_high(self):
+    def test_flame_effect_list(self):
+        self.assertCountEqual(
+            self.flame.effect_list,
+            [
+                "orange",
+                "red",
+                "green",
+                "blue",
+                "redgreen",
+                "redblue",
+                "bluegreen",
+                "redorange",
+                "greenorange",
+                "blueorange",
+            ],
+        )
+
+    def test_backlight_effect(self):
+        self.dps[BACKLIGHT_DPS] = "orange"
+        self.assertEqual(self.backlight.effect, "orange")
+
+    def test_flame_effect(self):
+        self.dps[FLAME_DPS] = "bluegreen"
+        self.assertEqual(self.flame.effect, "bluegreen")
+
+    async def test_set_backlight_effect(self):
         async with assert_device_properties_set(
         async with assert_device_properties_set(
-            self.subject._device,
-            {PRESET_DPS: "High"},
+            self.backlight._device,
+            {BACKLIGHT_DPS: "whiteblue"},
+        ):
+            await self.backlight.async_turn_on(effect="whiteblue")
+
+    async def test_set_flame_effect(self):
+        async with assert_device_properties_set(
+            self.flame._device,
+            {FLAME_DPS: "red"},
         ):
         ):
-            await self.subject.async_set_preset_mode("High")
+            await self.flame.async_turn_on(effect="red")