Skip to content

enum_.py fails to map boolean DP values when enumMappingMap keys are lowercase "true"/"false" (PIR motion stuck ON) #52

Description

@hiraniha

Hi Tuya team — I’ve found what looks like a bug in the enum strategy conversion that affects devices whose DP reports as a Python boolean (True/False) while the enumMappingMap keys are lowercase strings ("true"/"false"). This results in incorrect conversion and downstream integrations (e.g., Home Assistant) never receiving the “clear” state.

Affected file

tuya_sharing/strategy_repo/enum_.py

Current code:

if str(dp_value) in enum_mappings and "value" in enum_mappings[str(dp_value)]:
    status_value = enum_mappings[str(dp_value)]["value"]

Real config snippet (from PIR motion DP)

For my PIR motion datapoint, the SDK conversion config contains:

{
  "statusFormat": "{\"pir\":\"$\"}",
  "enumMappingMap": {
    "false": { "code": "pir", "value": "none" },
    "true":  { "code": "pir", "value": "pir"  }
  }
}

This means:

true => motion detected (pir)
false => clear (none)

Problem / root cause

When the device reports a boolean:

str(True) -> "True"
str(False) -> "False"

But enumMappingMap keys are "true" / "false" (lowercase).
So str(dp_value) in enum_mappings fails and the function falls back to convert_default_value(config_item).

In practice, this causes PIR motion to get stuck as "pir" even after the device reports False (clear), because the “clear” report isn’t mapped to "none".

Minimal reproduction

Call convert() with:

dp_item = ("pir", False)

config_item = { "statusFormat": "{"pir":"$"}", "enumMappingMap": {"false":{"code":"pir","value":"none"}, "true":{"code":"pir","value":"pir"}} }

Expected: ("pir", "none")
Actual: falls back to default and returns an incorrect value (in my case it stays "pir")

Best fix

Normalize boolean/string values before lookup (and optionally do a case-insensitive key fallback). For example:

key_raw = str(dp_value)
key_lc = key_raw.lower()

status_value = None
if key_raw in enum_mappings and "value" in enum_mappings[key_raw]:
    status_value = enum_mappings[key_raw]["value"]
elif key_lc in enum_mappings and "value" in enum_mappings[key_lc]:
    status_value = enum_mappings[key_lc]["value"]

if status_value is None:
    status_value = convert_default_value(config_item)

This preserves backward compatibility for mappings that already use "True"/"False" while fixing mappings that use "true"/"false".

An even more robust approach is to normalize all mapping keys once with str(k).lower() and look up using a normalized key (or casefold()), but the above is the smallest safe change.

Suggested tests

Add tests covering:

enumMappingMap keys "true"/"false" with dp_value as True/False

enumMappingMap keys "True"/"False" (existing behavior remains)

Happy to provide additional logs or a PR if you confirm preferred contribution approach.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions