Repository: zigpy/zha-device-handlers
Pull request #3827 (2025-02-03 22:10:48)

Prevent adding a Tuya DP converter twice for the same DP id
## Proposed change
<!--
  Explain your proposed change below.
-->
This prevents calling `tuya_dp` multiple times for the same DP id, which would previously overwrite previous mappings silently.
Two quirks were fixed since they had duplicated mappings. The values for those quirks were updated according to Z2M.


## Additional information
<!--
  Please include any additional information that is important to this PR.
  For example, if this PR is a potentially breaking change, mention that here.
  If this PR requires other PRs to be merged in HA Core or other projects, mention that.
  Lastly, if this PR fixes a specific issue, please include "Fixes #xxxx".
-->


## Checklist
<!--
  Put an 'x' in all boxes that apply.
  Note: You do not need to tick all boxes before creating a PR.
-->

- [x] The changes are tested and work correctly
- [x] `pre-commit` checks pass / the code has been formatted using Black
- [x] Tests have been added to verify that the new code works


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/zigpy/zha-device-handlers/pull/3827?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zigpy) Report
Attention: Patch coverage is `50.00000%` with `1 line` in your changes missing coverage. Please review.
> Project coverage is 90.90%. Comparing base [(`8b3560e`)](https://app.codecov.io/gh/zigpy/zha-device-handlers/commit/8b3560ee6d9cc105df7b698cec021ceacf14af1f?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zigpy) to head [(`1b69269`)](https://app.codecov.io/gh/zigpy/zha-device-handlers/commit/1b69269a7ad8a349753b7f404dbf242d2ef2c4dc?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zigpy).

| [Files with missing lines](https://app.codecov.io/gh/zigpy/zha-device-handlers/pull/3827?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zigpy) | Patch % | Lines |
|---|---|---|
| [zhaquirks/tuya/builder/\_\_init\_\_.py](https://app.codecov.io/gh/zigpy/zha-device-handlers/pull/3827?src=pr&el=tree&filepath=zhaquirks%2Ftuya%2Fbuilder%2F__init__.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zigpy#diff-emhhcXVpcmtzL3R1eWEvYnVpbGRlci9fX2luaXRfXy5weQ==) | 50.00% | [1 Missing :warning: ](https://app.codecov.io/gh/zigpy/zha-device-handlers/pull/3827?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zigpy) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##              dev    #3827      +/-   ##
==========================================
- Coverage   90.91%   90.90%   -0.01%     
==========================================
  Files         325      325              
  Lines       10566    10568       +2     
==========================================
+ Hits         9606     9607       +1     
- Misses        960      961       +1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/zigpy/zha-device-handlers/pull/3827?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zigpy).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zigpy).


Patch:
diff --git a/zhaquirks/tuya/builder/__init__.py b/zhaquirks/tuya/builder/__init__.py
index e8b40dc..fc11b93 100644
--- a/zhaquirks/tuya/builder/__init__.py
+++ b/zhaquirks/tuya/builder/__init__.py
@@ -502,6 +502,10 @@ class TuyaQuirkBuilder(QuirkBuilder):
         dp_handler: str = "_dp_2_attr_update",
     ) -> QuirkBuilder:  # fmt: skip
         """Add Tuya DP Converter."""
+
+        if dp_id in self.tuya_dp_to_attribute:
+            raise ValueError(f"DP {dp_id} is already mapped.")
+
         self.tuya_dp_to_attribute.update(
             {
                 dp_id: DPToAttributeMapping(
diff --git a/zhaquirks/tuya/tuya_motion.py b/zhaquirks/tuya/tuya_motion.py
index 840284f..d151c11 100644
--- a/zhaquirks/tuya/tuya_motion.py
+++ b/zhaquirks/tuya/tuya_motion.py
@@ -1192,7 +1192,7 @@ base_tuya_motion = (
         fallback_name="Maximum range",
     )
     .tuya_number(
-        dp_id=107,
+        dp_id=108,
         attribute_name="detection_distance_min",
         type=t.uint16_t,
         device_class=SensorDeviceClass.DISTANCE,


Test patch:
diff --git a/tests/test_tuya_builder.py b/tests/test_tuya_builder.py
index 97add16..b223b97 100644
--- a/tests/test_tuya_builder.py
+++ b/tests/test_tuya_builder.py
@@ -252,6 +252,23 @@ async def test_tuya_quirkbuilder(device_mock):
     assert tuya_listener.attribute_updates[0][1] == TestEnum.B
 
 
+async def test_tuya_quirkbuilder_duplicated_mappings(device_mock):
+    """Test that mapping the same DP multiple times will raise."""
+
+    registry = DeviceRegistry()
+
+    with pytest.raises(ValueError):
+        (
+            TuyaQuirkBuilder(
+                device_mock.manufacturer, device_mock.model, registry=registry
+            )
+            .tuya_battery(dp_id=1)
+            .tuya_onoff(dp_id=1)
+            .skip_configuration()
+            .add_to_registry()
+        )
+
+
 @pytest.mark.parametrize(
     "read_attr_spell,data_query_spell",
     [