Skip to content

Conversation

@baronorder
Copy link
Contributor

Its been a while since I was this deep in the weeds with Python but here we go...
Feel free to edit however you prefer!

This PR addresses an issue where using the mask parameter on data points of type: integer would result in correctable crashes and incorrect data parsing. This is required due to at least one device (Inkbird IVC-001W) that presents DPs in this fashion but I am sure that there are others out there.

Previously, the masking logic in device_config.py assumed that any masked value was a byte array (hex/base64). This caused two issues for packed integers (e.g., two 16-bit values packed into a 32-bit integer):

On read: The mask was ignored, returning the full raw integer instead of the specific bit range.

On write: The integration crashed with cannot convert 'float' object to bytes because it attempted to pass a numeric value into int.from_bytes.

Changes:

Updated get_value to check if the raw value is an integer. If so, it applies the mask and bit-shift directly without byte conversion.

Updated get_values_to_set to implement a "Read-Modify-Write" cycle for integers. It reads the current device state, clears the masked bits, and merges the new value, ensuring the unmasked parts of the integer remain untouched.

Backwards compatibility notes: The new logic only triggers if mask is defined and the raw value is an integer; existing hex/base64 implementations are unaffected.

Its been a while since I was this deep in the weeds with Python but here we go...

This PR addresses an issue where using the mask parameter on data points of type: integer would result in correctable crashes and incorrect data parsing. This is required due to at least one device (Inkbird IVC-001W) that presents DPs in this fashion but I am sure that there are others out there.

Previously, the masking logic in device_config.py assumed that any masked value was a byte array (hex/base64). This caused two issues for packed integers (e.g., two 16-bit values packed into a 32-bit integer):

On read: The mask was ignored, returning the full raw integer instead of the specific bit range.

On write: The integration crashed with cannot convert 'float' object to bytes because it attempted to pass a numeric value into int.from_bytes.

Changes:

Updated get_value to check if the raw value is an integer. If so, it applies the mask and bit-shift directly without byte conversion.

Updated get_values_to_set to implement a "Read-Modify-Write" cycle for integers. It reads the current device state, clears the masked bits, and merges the new value, ensuring the unmasked parts of the integer remain untouched.

Backwards compatibility notes: The new logic only triggers if mask is defined and the raw value is an integer; existing hex/base64 implementations are unaffected.
Simplify value decoding logic by removing conditional checks for raw types. decode_value is already designed to return the input if there is no decoding needed.

PR make-all#4220
@make-all make-all merged commit e504103 into make-all:main Jan 4, 2026
4 checks passed
@github-project-automation github-project-automation bot moved this from 👀 In review to ✅ Done in Tuya Local Jan 4, 2026
make-all added a commit that referenced this pull request Jan 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

2 participants