Просмотр исходного кода

feat (devices): add support for Tellur water valve

- add support in valve entity for separate boolean and integer control
  of the valve, to allow closing and opening without losing the open position

Issue #4467
Jason Rumney 1 день назад
Родитель
Сommit
632e5b4a8a

+ 1 - 0
custom_components/tuya_local/devices/README.md

@@ -798,6 +798,7 @@ to use it for other length timers.
 
 ### `valve`
 - **valve** (required, boolean or integer): a dp that reports the current state of the valve, and if not readonly, can also be used to set the state.  If a number, it should be a percentage between 0 and 100 indicating how far open the valve is.  If a boolean, it should indicate open (true) or closed (false).
+- **switch** (optional, boolean): if the valve dp is an integer, the valve may also have a boolean switch dp for closing and opening the valve without affecting the open valve position.
 
 ### `water_heater`
 - **current_temperature** (optional, number): a dp that reports the current water temperature.

+ 58 - 0
custom_components/tuya_local/devices/tellur_tll331501_watervalve.yaml

@@ -0,0 +1,58 @@
+name: Water valve
+products:
+  - id: llbov9p1f0lemzjz
+    manufacturer: Tellur
+    model: TL331501
+entities:
+  - entity: valve
+    class: water
+    dps:
+      - id: 1
+        type: boolean
+        name: switch
+      - id: 102
+        type: integer
+        name: valve
+        range:
+          min: 0
+          max: 100
+        mapping:
+          - step: 10
+  - entity: time
+    translation_key: timer
+    category: config
+    dps:
+      - id: 9
+        type: integer
+        name: second
+        range:
+          min: 0
+          max: 86399
+  - entity: select
+    translation_key: initial_state
+    category: config
+    dps:
+      - id: 38
+        type: string
+        name: option
+        mapping:
+          - dps_val: "off"
+            value: "off"
+          - dps_val: "on"
+            value: "on"
+          - dps_val: memory
+            value: memory
+  - entity: number
+    name: Maximum
+    category: config
+    icon: "mdi:pipe-valve"
+    dps:
+      - id: 101
+        type: integer
+        name: value
+        unit: "%"
+        range:
+          min: 0
+          max: 100
+        mapping:
+          - step: 10

+ 18 - 5
custom_components/tuya_local/valve.py

@@ -41,8 +41,12 @@ class TuyaLocalValve(TuyaLocalEntity, ValveEntity):
         super().__init__()
         dps_map = self._init_begin(device, config)
         self._valve_dp = dps_map.pop("valve")
+        self._switch_dp = dps_map.pop("switch")
         self._init_end(dps_map)
-        if not self._valve_dp.readonly:
+        if self._valve_dp is None:
+            raise AttributeError(f"{config.config_id} is missing valve dp")
+
+        if not self._valve_dp.readonly or self._switch_dp:
             self._attr_supported_features |= ValveEntityFeature.OPEN
             self._attr_supported_features |= ValveEntityFeature.CLOSE
             if self._valve_dp.type is int or (
@@ -92,11 +96,17 @@ class TuyaLocalValve(TuyaLocalEntity, ValveEntity):
     @property
     def is_closed(self):
         """Report whether the valve is closed."""
+        if self._switch_dp self._switch_dp.get_value(self._device) is False:
+            return True
         pos = self._valve_dp.get_value(self._device)
         return not pos
 
     async def async_open_valve(self):
         """Open the valve."""
+        if self._switch_dp:
+            await self._switch_dp.async_set_value(self._device, True)
+            if self._valve_dp.get_value(self._device):
+                return
         await self._valve_dp.async_set_value(
             self._device,
             100 if self.reports_position else True,
@@ -104,10 +114,13 @@ class TuyaLocalValve(TuyaLocalEntity, ValveEntity):
 
     async def async_close_valve(self):
         """Close the valve"""
-        await self._valve_dp.async_set_value(
-            self._device,
-            0 if self.reports_position else False,
-        )
+        if self._switch_dp:
+            await self._switch_dp.async_set_value(self._device, False)
+        else:
+            await self._valve_dp.async_set_value(
+                self._device,
+                0 if self.reports_position else False,
+            )
 
     async def async_set_valve_position(self, position):
         """Set the position of the valve"""

+ 1 - 1
tests/test_device_config.py

@@ -285,7 +285,7 @@ KNOWN_DPS = {
     },
     "valve": {
         "required": ["valve"],
-        "optional": [],
+        "optional": ["switch"],
     },
     "water_heater": {
         "required": [],