Browse Source

Add MoesHouse RGBW Nightlight + SmartPlug

Issue #126

Fixed some bugs in light component, and added tests for similar Digoo device.
Jason Rumney 3 years ago
parent
commit
13ca3ce8f5

+ 130 - 0
custom_components/tuya_local/devices/moes_rgb_socket.yaml

@@ -0,0 +1,130 @@
+name: MoesHouse RGB Socket
+primary_entity:
+  entity: switch
+  class: outlet
+  dps:
+    - id: 101
+      type: boolean
+      name: switch
+    - id: 105
+      type: integer
+      name: current_power_w
+      mapping:
+        - scale: 10
+secondary_entities:
+  - entity: light
+    name: Night Light
+    dps:
+      - id: 1
+        name: switch
+        type: boolean
+      - id: 2
+        name: color_mode
+        type: string
+        mapping:
+          - dps_val: white
+            value: white
+          - dps_val: colour
+            value: rgbw
+          - dps_val: scene
+            value: colorloop
+          - dps_val: scene_1
+            value: Scene 1
+          - dps_val: scene_2
+            value: Scene 2
+          - dps_val: scene_3
+            value: Scene 3
+          - dps_val: scene_4
+            value: Scene 4
+      - id: 3
+        name: brightness
+        type: integer
+        range:
+          min: 25
+          max: 255
+      - id: 4
+        name: unknown_4
+        type: integer
+      - id: 5
+        name: rgbhsv
+        type: hex
+        format:
+          - name: r
+            bytes: 1
+          - name: g
+            bytes: 1
+          - name: b
+            bytes: 1
+          - name: h
+            bytes: 2
+            range:
+              min: 0
+              max: 360
+          - name: s
+            bytes: 1
+          - name: v
+            bytes: 1
+      - id: 6
+        name: scene_data
+        type: hex
+      - id: 7
+        name: flash_scene_1
+        type: hex
+      - id: 8
+        name: flash_scene_2
+        type: hex
+      - id: 9
+        name: flash_scene_3
+        type: hex
+      - id: 10
+        name: flash_scene_4
+        type: hex
+  - entity: number
+    category: config
+    icon: "mdi:timer"
+    name: Timer
+    dps:
+      - id: 102
+        type: integer
+        name: value
+        unit: min
+        range:
+          min: 0
+          max: 86400
+        mapping:
+          - scale: 60
+            step: 60
+  - entity: sensor
+    name: Current
+    category: diagnostic
+    class: current
+    dps:
+      - id: 104
+        name: sensor
+        type: integer
+        class: measurement
+        unit: mA
+  - entity: sensor
+    name: Power
+    category: diagnostic
+    class: power
+    dps:
+      - id: 105
+        name: sensor
+        type: integer
+        class: measurement
+        unit: W
+        mapping:
+          - scale: 10
+  - entity: sensor
+    name: Voltage
+    category: diagnostic
+    class: voltage
+    dps:
+      - id: 106
+        name: sensor
+        type: integer
+        class: measurement
+        unit: V
+        mapping:
+          - scale: 10

+ 13 - 13
custom_components/tuya_local/generic/light.py

@@ -239,15 +239,16 @@ class TuyaLocalLight(TuyaLocalEntity, LightEntity):
                 }
 
         if self._brightness_dps:
-            bright = params.get(ATTR_BRIGHTNESS, 255)
-            bright_values = self._brightness_dps.get_values_to_set(
-                self._device,
-                bright,
-            )
-            settings = {
-                **settings,
-                **bright_values,
-            }
+            bright = params.get(ATTR_BRIGHTNESS, None if self._switch_dps else 255)
+            if bright is not None:
+                bright_values = self._brightness_dps.get_values_to_set(
+                    self._device,
+                    bright,
+                )
+                settings = {
+                    **settings,
+                    **bright_values,
+                }
 
         if self._rgbhsv_dps:
             rgbw = params.get(ATTR_RGBW_COLOR, None)
@@ -258,7 +259,7 @@ class TuyaLocalLight(TuyaLocalEntity, LightEntity):
                 rgbhsv = {
                     "r": rgb[0],
                     "g": rgb[1],
-                    "b": rgb[3],
+                    "b": rgb[2],
                     "h": hs[0],
                     "s": hs[1],
                     "v": rgbw[3],
@@ -274,10 +275,9 @@ class TuyaLocalLight(TuyaLocalEntity, LightEntity):
                         scale = r["max"] / 360
                     else:
                         scale = r["max"] / 255
-                    ordered[idx] = round(rgbhsv[n] * scale)
+                    ordered.append(round(rgbhsv[n] * scale))
                     idx += 1
-
-                binary = pack(format["format"], (*ordered,))
+                binary = pack(format["format"], *ordered)
                 color_dps = self._rgbhsv_dps.get_values_to_set(
                     self._device,
                     self._rgbhsv_dps.encode_value(binary),

+ 18 - 0
tests/const.py

@@ -1038,3 +1038,21 @@ KOGAN_GARAGE_DOOR_PAYLOAD = {
     "104": 100,
     "105": True,
 }
+
+MOES_RGB_SOCKET_PAYLOAD = {
+    "1": True,
+    "2": "colour",
+    "3": 255,
+    "4": 14,
+    "5": "ff99000024ffff",
+    "6": "bd76000168ffff",
+    "7": "ffff320100ff00",
+    "8": "ffff3203ff000000ff000000ff",
+    "9": "ffff3201ff0000",
+    "10": "ffff3205ff000000ff00ffff00ff00ff0000ff",
+    "101": True,
+    "102": 0,
+    "104": 0,
+    "105": 0,
+    "106": 2332,
+}

+ 37 - 1
tests/devices/test_digoo_dgsp01_dual_nightlight_switch.py

@@ -8,7 +8,7 @@ from homeassistant.components.light import (
     SUPPORT_EFFECT,
 )
 from ..const import DIGOO_DGSP01_SOCKET_PAYLOAD
-
+from ..helpers import assert_device_properties_set
 from ..mixins.switch import BasicSwitchTests
 from .base_device_tests import TuyaDeviceTestCase
 
@@ -114,6 +114,42 @@ class TestDigooNightlightSwitch(BasicSwitchTests, TuyaDeviceTestCase):
     def test_light_supported_features(self):
         self.assertEqual(self.light.supported_features, SUPPORT_EFFECT)
 
+    async def test_turn_on(self):
+        async with assert_device_properties_set(
+            self.light._device, {LIGHTSW_DPS: True}
+        ):
+            await self.light.async_turn_on()
+
+    async def test_turn_off(self):
+        async with assert_device_properties_set(
+            self.light._device, {LIGHTSW_DPS: False}
+        ):
+            await self.light.async_turn_off()
+
+    async def test_set_brightness(self):
+        async with assert_device_properties_set(
+            self.light._device,
+            {
+                LIGHTSW_DPS: True,
+                COLORMODE_DPS: "white",
+                BRIGHTNESS_DPS: 128,
+            },
+        ):
+            await self.light.async_turn_on(color_mode=COLOR_MODE_WHITE, brightness=128)
+
+    async def test_set_rgbw(self):
+        async with assert_device_properties_set(
+            self.light._device,
+            {
+                LIGHTSW_DPS: True,
+                COLORMODE_DPS: "colour",
+                RGBW_DPS: "ff000000006464",
+            },
+        ):
+            await self.light.async_turn_on(
+                color_mode=COLOR_MODE_RGBW, rgbw_color=(255, 0, 0, 255)
+            )
+
     def test_extra_state_attributes_set(self):
         self.dps[UNKNOWN32_DPS] = "32"
         self.dps[UNKNOWN33_DPS] = "33"

+ 221 - 0
tests/devices/test_moes_rgb_socket.py

@@ -0,0 +1,221 @@
+"""Tests for the MoesHouse RGBW smart socket."""
+from homeassistant.components.light import (
+    COLOR_MODE_RGBW,
+    COLOR_MODE_WHITE,
+    EFFECT_COLORLOOP,
+    SUPPORT_EFFECT,
+)
+from homeassistant.components.switch import DEVICE_CLASS_OUTLET
+from homeassistant.const import (
+    DEVICE_CLASS_CURRENT,
+    DEVICE_CLASS_POWER,
+    DEVICE_CLASS_VOLTAGE,
+    ELECTRIC_CURRENT_MILLIAMPERE,
+    ELECTRIC_POTENTIAL_VOLT,
+    POWER_WATT,
+    TIME_MINUTES,
+)
+
+from ..const import MOES_RGB_SOCKET_PAYLOAD
+from ..helpers import assert_device_properties_set
+from ..mixins.number import BasicNumberTests
+from ..mixins.sensor import MultiSensorTests
+from ..mixins.switch import BasicSwitchTests
+from .base_device_tests import TuyaDeviceTestCase
+
+LIGHT_DPS = "1"
+MODE_DPS = "2"
+BRIGHTNESS_DPS = "3"
+UNKNOWN4_DPS = "4"
+RGBW_DPS = "5"
+SCENE_DPS = "6"
+SCENE1_DPS = "7"
+SCENE2_DPS = "8"
+SCENE3_DPS = "9"
+SCENE4_DPS = "10"
+SWITCH_DPS = "101"
+TIMER_DPS = "102"
+CURRENT_DPS = "104"
+POWER_DPS = "105"
+VOLTAGE_DPS = "106"
+
+
+class TestMoesRGBWSocket(
+    BasicNumberTests,
+    MultiSensorTests,
+    BasicSwitchTests,
+    TuyaDeviceTestCase,
+):
+    __test__ = True
+
+    def setUp(self):
+        self.setUpForConfig("moes_rgb_socket.yaml", MOES_RGB_SOCKET_PAYLOAD)
+        self.light = self.entities.get("light_night_light")
+
+        self.setUpBasicSwitch(
+            SWITCH_DPS,
+            self.entities.get("switch"),
+            device_class=DEVICE_CLASS_OUTLET,
+            power_dps=POWER_DPS,
+            power_scale=10,
+        )
+        self.setUpBasicNumber(
+            TIMER_DPS,
+            self.entities.get("number_timer"),
+            max=1440.0,
+            unit=TIME_MINUTES,
+            scale=60,
+        )
+        self.setUpMultiSensors(
+            [
+                {
+                    "name": "sensor_voltage",
+                    "dps": VOLTAGE_DPS,
+                    "unit": ELECTRIC_POTENTIAL_VOLT,
+                    "device_class": DEVICE_CLASS_VOLTAGE,
+                    "state_class": "measurement",
+                    "testdata": (2300, 230.0),
+                },
+                {
+                    "name": "sensor_current",
+                    "dps": CURRENT_DPS,
+                    "unit": ELECTRIC_CURRENT_MILLIAMPERE,
+                    "device_class": DEVICE_CLASS_CURRENT,
+                    "state_class": "measurement",
+                },
+                {
+                    "name": "sensor_power",
+                    "dps": POWER_DPS,
+                    "unit": POWER_WATT,
+                    "device_class": DEVICE_CLASS_POWER,
+                    "state_class": "measurement",
+                    "testdata": (1234, 123.4),
+                },
+            ]
+        )
+        self.mark_secondary(
+            [
+                "number_timer",
+                "sensor_current",
+                "sensor_power",
+                "sensor_voltage",
+            ]
+        )
+
+    def test_light_is_on(self):
+        self.dps[LIGHT_DPS] = True
+        self.assertTrue(self.light.is_on)
+        self.dps[LIGHT_DPS] = False
+        self.assertFalse(self.light.is_on)
+
+    def test_light_brightness(self):
+        self.dps[BRIGHTNESS_DPS] = 45
+        self.assertEqual(self.light.brightness, 45)
+
+    def test_light_color_mode(self):
+        self.dps[MODE_DPS] = "colour"
+        self.assertEqual(self.light.color_mode, COLOR_MODE_RGBW)
+        self.dps[MODE_DPS] = "white"
+        self.assertEqual(self.light.color_mode, COLOR_MODE_WHITE)
+        self.dps[MODE_DPS] = "scene"
+        self.assertEqual(self.light.color_mode, COLOR_MODE_RGBW)
+        self.dps[MODE_DPS] = "scene_1"
+        self.assertEqual(self.light.color_mode, COLOR_MODE_RGBW)
+        self.dps[MODE_DPS] = "scene_2"
+        self.assertEqual(self.light.color_mode, COLOR_MODE_RGBW)
+        self.dps[MODE_DPS] = "scene_3"
+        self.assertEqual(self.light.color_mode, COLOR_MODE_RGBW)
+        self.dps[MODE_DPS] = "scene_4"
+        self.assertEqual(self.light.color_mode, COLOR_MODE_RGBW)
+
+    def test_light_rgbw_color(self):
+        self.dps[RGBW_DPS] = "ffff00003cffff"
+        self.assertSequenceEqual(
+            self.light.rgbw_color,
+            (255, 255, 0, 255),
+        )
+
+    def test_light_effect_list(self):
+        self.assertCountEqual(
+            self.light.effect_list,
+            [
+                EFFECT_COLORLOOP,
+                "Scene 1",
+                "Scene 2",
+                "Scene 3",
+                "Scene 4",
+            ],
+        )
+
+    def test_light_effect(self):
+        self.dps[MODE_DPS] = "scene"
+        self.assertEqual(self.light.effect, EFFECT_COLORLOOP)
+        self.dps[MODE_DPS] = "scene_1"
+        self.assertEqual(self.light.effect, "Scene 1")
+        self.dps[MODE_DPS] = "scene_2"
+        self.assertEqual(self.light.effect, "Scene 2")
+        self.dps[MODE_DPS] = "scene_3"
+        self.assertEqual(self.light.effect, "Scene 3")
+        self.dps[MODE_DPS] = "scene_4"
+        self.assertEqual(self.light.effect, "Scene 4")
+
+    def test_light_supported_color_modes(self):
+        self.assertCountEqual(
+            self.light.supported_color_modes,
+            {COLOR_MODE_RGBW, COLOR_MODE_WHITE},
+        )
+
+    def test_light_supported_features(self):
+        self.assertEqual(self.light.supported_features, SUPPORT_EFFECT)
+
+    async def test_turn_on(self):
+        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: True}):
+            await self.light.async_turn_on()
+
+    async def test_turn_off(self):
+        async with assert_device_properties_set(self.light._device, {LIGHT_DPS: False}):
+            await self.light.async_turn_off()
+
+    async def test_set_brightness(self):
+        async with assert_device_properties_set(
+            self.light._device,
+            {
+                LIGHT_DPS: True,
+                MODE_DPS: "white",
+                BRIGHTNESS_DPS: 128,
+            },
+        ):
+            await self.light.async_turn_on(color_mode=COLOR_MODE_WHITE, brightness=128)
+
+    async def test_set_rgbw(self):
+        async with assert_device_properties_set(
+            self.light._device,
+            {
+                LIGHT_DPS: True,
+                MODE_DPS: "colour",
+                RGBW_DPS: "ff00000000ffff",
+            },
+        ):
+            await self.light.async_turn_on(
+                color_mode=COLOR_MODE_RGBW, rgbw_color=(255, 0, 0, 255)
+            )
+
+    def test_extra_state_attributes_set(self):
+        self.dps[UNKNOWN4_DPS] = 4
+        self.dps[SCENE_DPS] = "scene"
+        self.dps[SCENE1_DPS] = "scene1"
+        self.dps[SCENE2_DPS] = "scene2"
+        self.dps[SCENE3_DPS] = "scene3"
+        self.dps[SCENE4_DPS] = "scene4"
+
+        self.assertDictEqual(
+            self.light.extra_state_attributes,
+            {
+                "unknown_4": 4,
+                "scene_data": "scene",
+                "flash_scene_1": "scene1",
+                "flash_scene_2": "scene2",
+                "flash_scene_3": "scene3",
+                "flash_scene_4": "scene4",
+            },
+        )