Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions tinytuya/Cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -519,10 +519,23 @@ def getdevices(self, verbose=False, oldlist=[], include_map=False):
for dev in changed_devices:
if 'product_id' in dev and dev['product_id'] == productid:
dev['mapping'] = mappings[productid]
# also set unchanged devices just in case the mapping changed
for dev in unchanged_devices:
if 'product_id' in dev and dev['product_id'] == productid:
dev['mapping'] = mappings[productid]
# also set unchanged devices just in case the mapping changed,
# but only if the new mapping is non-empty (guard against overwriting
# a good cached mapping with an empty result from a transient API failure)
if mappings[productid]:
for dev in unchanged_devices:
if 'product_id' in dev and dev['product_id'] == productid:
dev['mapping'] = mappings[productid]

# Fallback: restore old mapping for changed devices that got no mapping back
# (API failure, rate limit, etc.) — use the mapping from oldlist if available
for dev in changed_devices:
if not dev.get('mapping'):
dev_id = dev.get('id')
if dev_id and dev_id in old_devices:
old_mapping = old_devices[dev_id].get('mapping')
if old_mapping:
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback condition if not dev.get('mapping'): treats an empty mapping {} as “missing” and will restore the old mapping, which can override an intentional empty mapping returned by getmapping() for devices with no DPs (e.g., cloud code 2009 sets self.mappings[productid] = {}). Consider only falling back when the mapping key is absent or explicitly None, and when restoring from oldlist treat None as the sentinel (so an empty dict mapping can be preserved/restored too).

Suggested change
# (API failure, rate limit, etc.) — use the mapping from oldlist if available
for dev in changed_devices:
if not dev.get('mapping'):
dev_id = dev.get('id')
if dev_id and dev_id in old_devices:
old_mapping = old_devices[dev_id].get('mapping')
if old_mapping:
# (API failure, rate limit, etc.) — use the mapping from oldlist if available.
# Only fallback when the mapping is missing or explicitly None, so that an
# intentionally empty mapping {} is preserved.
for dev in changed_devices:
if 'mapping' not in dev or dev['mapping'] is None:
dev_id = dev.get('id')
if dev_id and dev_id in old_devices:
old_mapping = old_devices[dev_id].get('mapping')
if old_mapping is not None:

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — applied in 26e2929. Changed to 'mapping' not in dev or dev['mapping'] is None so that an intentionally empty {} (API code 2009, device with no DPs) is preserved rather than replaced by a stale old mapping. Similarly updated the old-mapping guard to if old_mapping is not None: so an old {} mapping can also be restored correctly.

dev['mapping'] = old_mapping

log.debug( 'changed: %d', len(changed_devices) )
log.debug( 'unchanged: %d', len(unchanged_devices) )
Expand Down
Loading