Browse Source

feat(number): allow separate whole and decimal portions of a number

Use case: an alarm clock has FM frequency encoded in two bytes,
top byte is whole, lower byte is decimal. Mask can be used to
extract these, but we need to combine them back together into
a number that HA can understand.

PR #4825
Jason Rumney 2 months ago
parent
commit
0073e51acc

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

@@ -744,6 +744,7 @@ no information will be available about which specific credential was used to unl
     This may be used as an alternative to a range setting on the **value** dp if the range is dynamic
 - **maximum** (optional, number): a dp that reports the maximum the number can be set to.
     This may be used as an alternative to a range setting on the **value** dp if the range is dynamic
+- **decimal** (optional, number): a dp that is added to the value to specify the decimal portion of the number separately from the whole number portion. This must be scaled into a decimal number range.
 
 ### `remote`
 - **send** (required, accepts a string): a dp to send remote codes.

+ 19 - 2
custom_components/tuya_local/number.py

@@ -50,6 +50,7 @@ class TuyaLocalNumber(TuyaLocalEntity, NumberEntity):
         self._unit_dps = dps_map.pop("unit", None)
         self._min_dps = dps_map.pop("minimum", None)
         self._max_dps = dps_map.pop("maximum", None)
+        self._decimal_dps = dps_map.pop("decimal", None)
         self._init_end(dps_map)
 
     @property
@@ -110,9 +111,25 @@ class TuyaLocalNumber(TuyaLocalEntity, NumberEntity):
     @property
     def native_value(self):
         """Return the current value of the number."""
-        return self._value_dps.get_value(self._device)
+        val = self._value_dps.get_value(self._device)
+        if self._decimal_dps is not None:
+            decimal = self._decimal_dps.get_value(self._device)
+            if decimal is not None:
+                val = val + decimal
+        return val
 
     async def async_set_native_value(self, value):
         """Set the number."""
         _LOGGER.info("%s setting value to %s", self._config.config_id, value)
-        await self._value_dps.async_set_value(self._device, value)
+        settings = {}
+        if self._decimal_dps is not None:
+            whole = int(value)
+            decimal = value - whole
+            settings = self._decimal_dps.get_values_to_set(self._device, decimal)
+            value = whole
+
+        settings = settings | self._value_dps.get_values_to_set(
+            self._device, value, settings
+        )
+
+        await self._device.async_set_properties(settings)

+ 1 - 1
tests/test_device_config.py

@@ -261,7 +261,7 @@ KNOWN_DPS = {
     },
     "number": {
         "required": ["value"],
-        "optional": ["unit", "minimum", "maximum"],
+        "optional": ["unit", "minimum", "maximum", "decimal"],
     },
     "remote": {
         "required": ["send"],