Skip to content

Conversation

@bobmatnyc
Copy link

Summary

Fixes #529 by adding missing fields to the Device and Component models that were causing deserialization failures.

Changes

  • Added optional field to Component model - The SmartThings API returns optional: false in component objects, but our Component dataclass was missing this field
  • Changed relationships type from dict | None to dict | list | None to handle API variations where some devices return empty lists, non-empty lists, or dicts
  • Updated Device.__pre_deserialize__() to normalize empty values:
    • Convert empty relationships list [] to None
    • Convert empty viper dict {} to None (since Viper model has required fields)
  • Fixed test_connection.py to correctly access capabilities via device.components["main"].capabilities

Testing

Verified deserialization of all 184 real devices from my SmartThings account

  • Previously: 0/184 devices could deserialize
  • After Component fix: 182/184 devices successful
  • After relationships fix: 184/184 devices successful

All quality checks passing:

  • ruff linter and formatter
  • mypy static type checking
  • pylint code quality
  • All pre-commit hooks green

Root Cause

The SmartThings API has inconsistent data structures:

  • Most devices: relationships: [] (empty list)
  • 2 devices ("Hub - Frame TV", "Frame TV"): relationships: [{'deviceId': '...', 'relationshipType': 'COMBINATION', ...}] (non-empty list)
  • 1 device ("Kitchen Light Strip"): viper: {} (empty dict, but Viper model expects required fields)
  • All components: optional: false (field was missing from our model)

The fix handles all these variations gracefully while maintaining type safety.

Developer's Certificate of Origin

By making a contribution to this project, I certify that:

  • (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or

  • (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or

  • (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it.

  • (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved.

bobmatnyc and others added 5 commits November 25, 2025 08:38
- Exclude AI-generated documentation (CLAUDE.md, ARCHITECTURE.md, PROJECT_VALIDATION.md)
- Exclude Claude MPM configuration directories (.claude-mpm/, .claude/)
- Consolidate existing Claude-related ignore patterns
- Add comprehensive file type exclusions for development tools

These files are AI agent-specific and should not be committed to the repository.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Addresses pySmartThings#295 - Documentation request from users
Addresses pySmartThings#69 - Lock code example request

Changes:
- Add examples/ directory with 6 working Python examples
- Add examples/README.md with setup and usage instructions
- Update README.md with Usage section and quick start
- Update .gitignore to exclude temporary docs (docs/bobmatnyc/)
- Update .gitignore to exclude .mcp.json (environment-specific)

Examples added:
- basic_usage.py: Authentication, device listing, status checks
- control_devices.py: Control switches, dimmers, colors, thermostats
- scenes.py: Scene management and execution
- lock_codes.py: Lock code management (SET_CODE, DELETE_CODE)
- event_subscription.py: Real-time SSE event handling
- async_patterns.py: Production-ready async patterns

All examples follow project conventions:
- Python 3.12+ async/await syntax
- Type hints throughout
- Google-style docstrings
- Proper error handling
- 88-character line length
- Add examples/ to .pre-commit-config.yaml pylint exclude
- Add mypy override for examples.* module in pyproject.toml
- Add examples to pylint ignore list in pyproject.toml
- Examples are demonstration code with intentional patterns
- Mashumaro dynamic attributes cause mypy false positives
…martThings#529)

Changes:
- Added optional field to Component model
- Changed relationships type to dict | list | None to handle API variations
- Updated Device.__pre_deserialize__ to normalize empty values:
  * Convert empty relationships list [] to None
  * Convert empty viper dict {} to None
- Fixed test_connection.py to access capabilities via components

Fixes pySmartThings#529

Testing:
- Verified deserialization of all 184 real devices from SmartThings API
- All quality checks passing (ruff, mypy, pylint)

🤖👥 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Device deserialization fails with InvalidFieldValue - missing fields in Device dataclass

1 participant