nwp500-python 1.2.3__tar.gz → 2.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. nwp500_python-2.0.0/BREAKING_CHANGES_V3.md +122 -0
  2. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/CHANGELOG.rst +59 -0
  3. nwp500_python-2.0.0/MIGRATION.md +217 -0
  4. {nwp500_python-1.2.3/src/nwp500_python.egg-info → nwp500_python-2.0.0}/PKG-INFO +1 -1
  5. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/DEVICE_STATUS_FIELDS.rst +16 -14
  6. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/event_emitter_demo.py +7 -5
  7. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/periodic_requests.py +1 -1
  8. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/tou_schedule_example.py +6 -1
  9. nwp500_python-2.0.0/src/nwp500/__init__.py +160 -0
  10. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/src/nwp500/models.py +109 -6
  11. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/src/nwp500/mqtt_client.py +8 -4
  12. {nwp500_python-1.2.3 → nwp500_python-2.0.0/src/nwp500_python.egg-info}/PKG-INFO +1 -1
  13. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/src/nwp500_python.egg-info/SOURCES.txt +2 -0
  14. nwp500_python-1.2.3/src/nwp500/__init__.py +0 -102
  15. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/.coveragerc +0 -0
  16. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/.github/copilot-instructions.md +0 -0
  17. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/.github/workflows/ci.yml +0 -0
  18. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/.github/workflows/release.yml +0 -0
  19. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/.gitignore +0 -0
  20. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/.pre-commit-config.yaml +0 -0
  21. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/.readthedocs.yml +0 -0
  22. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/AUTHORS.rst +0 -0
  23. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/CONTRIBUTING.rst +0 -0
  24. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/LICENSE.txt +0 -0
  25. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/Makefile +0 -0
  26. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/README.rst +0 -0
  27. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/RELEASE.md +0 -0
  28. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/API_CLIENT.rst +0 -0
  29. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/API_REFERENCE.rst +0 -0
  30. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/AUTHENTICATION.rst +0 -0
  31. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/AUTO_RECOVERY.rst +0 -0
  32. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/AUTO_RECOVERY_QUICK.rst +0 -0
  33. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/COMMAND_QUEUE.rst +0 -0
  34. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/DEVELOPMENT.rst +0 -0
  35. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/DEVICE_FEATURE_FIELDS.rst +0 -0
  36. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/ENERGY_MONITORING.rst +0 -0
  37. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/ERROR_CODES.rst +0 -0
  38. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/EVENT_EMITTER.rst +0 -0
  39. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/FIRMWARE_TRACKING.rst +0 -0
  40. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/MQTT_CLIENT.rst +0 -0
  41. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/MQTT_MESSAGES.rst +0 -0
  42. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/Makefile +0 -0
  43. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/_static/.gitignore +0 -0
  44. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/authors.rst +0 -0
  45. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/changelog.rst +0 -0
  46. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/conf.py +0 -0
  47. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/contributing.rst +0 -0
  48. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/index.rst +0 -0
  49. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/license.rst +0 -0
  50. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/openapi.yaml +0 -0
  51. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/readme.rst +0 -0
  52. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/docs/requirements.txt +0 -0
  53. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/.ruff.toml +0 -0
  54. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/README.md +0 -0
  55. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/anti_legionella_example.py +0 -0
  56. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/api_client_example.py +0 -0
  57. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/auth_constructor_example.py +0 -0
  58. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/authenticate.py +0 -0
  59. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/auto_recovery_example.py +0 -0
  60. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/combined_callbacks.py +0 -0
  61. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/command_queue_demo.py +0 -0
  62. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/device_feature_callback.py +0 -0
  63. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/device_status_callback.py +0 -0
  64. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/device_status_callback_debug.py +0 -0
  65. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/energy_usage_example.py +0 -0
  66. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/improved_auth_pattern.py +0 -0
  67. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/mask.py +0 -0
  68. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/mqtt_client_example.py +0 -0
  69. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/periodic_device_info.py +0 -0
  70. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/power_control_example.py +0 -0
  71. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/reconnection_demo.py +0 -0
  72. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/reservation_schedule_example.py +0 -0
  73. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/set_dhw_temperature_example.py +0 -0
  74. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/set_mode_example.py +0 -0
  75. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/simple_auto_recovery.py +0 -0
  76. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/simple_periodic_info.py +0 -0
  77. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/simple_periodic_status.py +0 -0
  78. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/test_api_client.py +0 -0
  79. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/test_mqtt_connection.py +0 -0
  80. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/test_mqtt_messaging.py +0 -0
  81. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/examples/test_periodic_minimal.py +0 -0
  82. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/pyproject.toml +0 -0
  83. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/scripts/format.py +0 -0
  84. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/scripts/lint.py +0 -0
  85. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/scripts/setup-dev.py +0 -0
  86. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/setup.cfg +0 -0
  87. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/setup.py +0 -0
  88. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/src/nwp500/api_client.py +0 -0
  89. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/src/nwp500/auth.py +0 -0
  90. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/src/nwp500/cli.py +0 -0
  91. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/src/nwp500/config.py +0 -0
  92. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/src/nwp500/constants.py +0 -0
  93. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/src/nwp500/events.py +0 -0
  94. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/src/nwp500_python.egg-info/dependency_links.txt +0 -0
  95. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/src/nwp500_python.egg-info/entry_points.txt +0 -0
  96. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/src/nwp500_python.egg-info/not-zip-safe +0 -0
  97. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/src/nwp500_python.egg-info/requires.txt +0 -0
  98. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/src/nwp500_python.egg-info/top_level.txt +0 -0
  99. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/tests/conftest.py +0 -0
  100. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/tests/test_api_helpers.py +0 -0
  101. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/tests/test_command_queue.py +0 -0
  102. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/tests/test_events.py +0 -0
  103. {nwp500_python-1.2.3 → nwp500_python-2.0.0}/tox.ini +0 -0
@@ -0,0 +1,122 @@
1
+ # Breaking Changes for v3.0.0
2
+
3
+ This document outlines the breaking changes planned for nwp500-python v3.0.0.
4
+
5
+ ## Overview
6
+
7
+ Version 3.0.0 will be the first major version to include breaking changes since the library's initial release. The primary focus is removing deprecated functionality and improving type safety.
8
+
9
+ ## Scheduled Removals
10
+
11
+ ### OperationMode Enum Removal
12
+
13
+ **Target**: v3.0.0
14
+ **Current Status**: Deprecated in v2.0.0
15
+ **Migration Period**: v2.x series (minimum 6 months)
16
+
17
+ The original `OperationMode` enum will be completely removed and replaced with:
18
+ - `DhwOperationSetting` for user-configured mode preferences
19
+ - `CurrentOperationMode` for real-time operational states
20
+
21
+ #### Impact Assessment
22
+ - **High Impact**: Code using direct enum comparisons
23
+ - **Medium Impact**: Type hints and function signatures
24
+ - **Low Impact**: Display-only usage (`.name` attribute)
25
+
26
+ #### Required Actions
27
+ 1. Replace all `OperationMode` imports with specific enum imports
28
+ 2. Update enum comparisons to use appropriate enum type
29
+ 3. Update type hints in function signatures
30
+ 4. Remove any `OperationMode` references from custom code
31
+
32
+ #### Migration Tools Available
33
+ - `MIGRATION.md` - Comprehensive migration guide
34
+ - `migrate_operation_mode_usage()` - Programmatic guidance
35
+ - `enable_deprecation_warnings()` - Runtime warning system
36
+ - Updated documentation with new enum usage patterns
37
+
38
+ ## Pre-3.0 Checklist
39
+
40
+ Before releasing v3.0.0, ensure:
41
+
42
+ ### Code Removal
43
+ - [ ] Remove `OperationMode` enum class from `models.py`
44
+ - [ ] Remove `OperationMode` from package exports (`__init__.py`)
45
+ - [ ] Remove deprecation warning infrastructure
46
+ - [ ] Update all internal references to use new enums
47
+
48
+ ### Documentation Updates
49
+ - [ ] Update all documentation to remove `OperationMode` references
50
+ - [ ] Update `MIGRATION.md` to reflect completed migration
51
+ - [ ] Update API documentation
52
+ - [ ] Update example scripts if any still reference old enum
53
+
54
+ ### Testing
55
+ - [ ] Ensure all tests pass without `OperationMode`
56
+ - [ ] Add tests to verify enum removal
57
+ - [ ] Test that import errors are clear and helpful
58
+ - [ ] Validate all examples work with new enums only
59
+
60
+ ### Communication
61
+ - [ ] Update CHANGELOG.rst with breaking changes
62
+ - [ ] Release notes highlighting breaking changes
63
+ - [ ] GitHub release with migration guidance
64
+ - [ ] Consider blog post or announcement for major users
65
+
66
+ ## Timeline
67
+
68
+ ```
69
+ v2.0.0 (Current)
70
+ ├── OperationMode deprecated
71
+ ├── New enums introduced
72
+ ├── Migration tools provided
73
+ └── Backward compatibility maintained
74
+
75
+ v2.1.0 (Optional)
76
+ ├── Optional deprecation warnings
77
+ ├── Enhanced migration tools
78
+ └── Continued compatibility
79
+
80
+ v2.x.x (6+ months)
81
+ ├── Migration period
82
+ ├── User feedback collection
83
+ └── Migration assistance
84
+
85
+ v3.0.0 (Target)
86
+ ├── OperationMode removed
87
+ ├── Breaking changes implemented
88
+ └── Type safety improvements
89
+ ```
90
+
91
+ ## Rollback Plan
92
+
93
+ If breaking changes cause significant issues:
94
+
95
+ 1. **Emergency Patch**: Revert breaking changes in v3.0.1
96
+ 2. **Deprecation Extension**: Extend deprecation period to v4.0.0
97
+ 3. **Enhanced Migration**: Provide additional migration tools
98
+ 4. **Communication**: Clear messaging about timeline changes
99
+
100
+ ## Version Support
101
+
102
+ - **v2.x**: Long-term support with security fixes
103
+ - **v3.x**: Current development branch
104
+ - **v1.x**: End-of-life (security fixes only if critical)
105
+
106
+ ## Migration Support
107
+
108
+ Migration support will be available:
109
+ - **During v2.x**: Full backward compatibility + new enums
110
+ - **During v3.0 beta**: Migration warnings and testing
111
+ - **After v3.0**: Documentation and examples only
112
+
113
+ ## Contact
114
+
115
+ For questions about breaking changes or migration assistance:
116
+ - File GitHub issues with "migration" label
117
+ - Reference `MIGRATION.md` for detailed guidance
118
+ - Use `migrate_operation_mode_usage()` for programmatic help
119
+
120
+ ---
121
+
122
+ *This document will be updated as v3.0.0 approaches with more specific details and timelines.*
@@ -2,6 +2,65 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
+ Version 2.0.0 (Unreleased)
6
+ ==========================
7
+
8
+ **Breaking Changes (Planned for v3.0.0)**
9
+
10
+ - **DEPRECATION**: ``OperationMode`` enum is deprecated and will be removed in v3.0.0
11
+
12
+ - Use ``DhwOperationSetting`` for user-configured mode preferences (values 1-6)
13
+ - Use ``CurrentOperationMode`` for real-time operational states (values 0, 32, 64, 96)
14
+ - See ``MIGRATION.md`` for detailed migration guide
15
+
16
+ Added
17
+ -----
18
+
19
+ - **Enhanced Type Safety**: Split ``OperationMode`` into semantically distinct enums
20
+
21
+ - ``DhwOperationSetting``: User-configured mode preferences (HEAT_PUMP, ELECTRIC, ENERGY_SAVER, HIGH_DEMAND, VACATION, POWER_OFF)
22
+ - ``CurrentOperationMode``: Real-time operational states (STANDBY, HEAT_PUMP_MODE, HYBRID_EFFICIENCY_MODE, HYBRID_BOOST_MODE)
23
+ - Prevents accidental comparison of user preferences with real-time states
24
+ - Better IDE support with more specific enum types
25
+
26
+ - **Migration Support**: Comprehensive tools for smooth migration
27
+
28
+ - ``migrate_operation_mode_usage()`` helper function with programmatic guidance
29
+ - ``MIGRATION.md`` with step-by-step migration instructions
30
+ - Value mappings and common usage pattern examples
31
+ - Backward compatibility preservation during transition
32
+
33
+ - **Documentation Updates**: Updated all documentation to reflect new enum structure
34
+
35
+ - ``DEVICE_STATUS_FIELDS.rst`` updated with new enum types
36
+ - Code examples use new enums with proper imports
37
+ - Clear distinction between configuration vs real-time status
38
+
39
+ Changed
40
+ -------
41
+
42
+ - **DeviceStatus Model**: Updated to use specific enum types
43
+
44
+ - ``operationMode`` field now uses ``CurrentOperationMode`` type
45
+ - ``dhwOperationSetting`` field now uses ``DhwOperationSetting`` type
46
+ - Maintains backward compatibility through value preservation
47
+
48
+ - **Example Scripts**: Updated to demonstrate new enum usage
49
+
50
+ - ``event_emitter_demo.py`` updated to use ``CurrentOperationMode``
51
+ - Fixed incorrect enum references (HEAT_PUMP_ONLY → HEAT_PUMP_MODE)
52
+ - All examples remain functional with new type system
53
+
54
+ Deprecated
55
+ ----------
56
+
57
+ - **OperationMode enum**: Will be removed in v3.0.0
58
+
59
+ - All functionality preserved for backward compatibility
60
+ - Migration guide available in ``MIGRATION.md``
61
+ - Helper function ``migrate_operation_mode_usage()`` provides guidance
62
+ - Original enum remains available during transition period
63
+
5
64
  Version 1.2.2 (2025-10-17)
6
65
  ==========================
7
66
 
@@ -0,0 +1,217 @@
1
+ # Migration Guide: OperationMode → DhwOperationSetting & CurrentOperationMode
2
+
3
+ ## Overview
4
+
5
+ In version 2.x of nwp500-python, the `OperationMode` enum has been split into two more specific enums to improve code clarity and type safety:
6
+
7
+ - **`DhwOperationSetting`**: User-configured mode preferences (values 1-6)
8
+ - **`CurrentOperationMode`**: Real-time operational states (values 0, 32, 64, 96)
9
+
10
+ This change clarifies the distinction between "what mode the user has configured" vs "what the device is doing right now."
11
+
12
+ ## Why This Change?
13
+
14
+ The original `OperationMode` enum mixed two different concepts:
15
+ 1. **User preferences** (1-6): What heating mode should be used when heating is needed
16
+ 2. **Real-time status** (0, 32, 64, 96): What the device is actively doing right now
17
+
18
+ This led to confusion and potential bugs. The new enums provide:
19
+ - **Type safety**: Can't accidentally compare user preferences with real-time states
20
+ - **Clarity**: Clear distinction between configuration and current operation
21
+ - **Better IDE support**: More specific autocomplete and type checking
22
+
23
+ ## Migration Steps
24
+
25
+ ### 1. Update Imports
26
+
27
+ **Before:**
28
+ ```python
29
+ from nwp500 import OperationMode
30
+ ```
31
+
32
+ **After:**
33
+ ```python
34
+ from nwp500 import DhwOperationSetting, CurrentOperationMode
35
+ # or keep the old import during transition:
36
+ from nwp500 import OperationMode, DhwOperationSetting, CurrentOperationMode
37
+ ```
38
+
39
+ ### 2. Update DHW Operation Setting Comparisons
40
+
41
+ **Before:**
42
+ ```python
43
+ if status.dhwOperationSetting == OperationMode.ENERGY_SAVER:
44
+ print("Device configured for Energy Saver mode")
45
+
46
+ if status.dhwOperationSetting == OperationMode.POWER_OFF:
47
+ print("Device is powered off")
48
+ ```
49
+
50
+ **After:**
51
+ ```python
52
+ if status.dhwOperationSetting == DhwOperationSetting.ENERGY_SAVER:
53
+ print("Device configured for Energy Saver mode")
54
+
55
+ if status.dhwOperationSetting == DhwOperationSetting.POWER_OFF:
56
+ print("Device is powered off")
57
+ ```
58
+
59
+ ### 3. Update Operation Mode Comparisons
60
+
61
+ **Before:**
62
+ ```python
63
+ if status.operationMode == OperationMode.STANDBY:
64
+ print("Device is idle")
65
+
66
+ if status.operationMode == OperationMode.HEAT_PUMP_MODE:
67
+ print("Heat pump is running")
68
+ ```
69
+
70
+ **After:**
71
+ ```python
72
+ if status.operationMode == CurrentOperationMode.STANDBY:
73
+ print("Device is idle")
74
+
75
+ if status.operationMode == CurrentOperationMode.HEAT_PUMP_MODE:
76
+ print("Heat pump is running")
77
+ ```
78
+
79
+ ### 4. Update Type Hints
80
+
81
+ **Before:**
82
+ ```python
83
+ def handle_mode_change(old_mode: OperationMode, new_mode: OperationMode):
84
+ pass
85
+
86
+ def check_dhw_setting(setting: OperationMode) -> bool:
87
+ pass
88
+ ```
89
+
90
+ **After:**
91
+ ```python
92
+ def handle_mode_change(old_mode: CurrentOperationMode, new_mode: CurrentOperationMode):
93
+ pass
94
+
95
+ def check_dhw_setting(setting: DhwOperationSetting) -> bool:
96
+ pass
97
+ ```
98
+
99
+ ### 5. Update Command Sending Logic
100
+
101
+ **Before:**
102
+ ```python
103
+ # Vacation mode check
104
+ if mode_id == OperationMode.VACATION.value:
105
+ # handle vacation days parameter
106
+ ```
107
+
108
+ **After:**
109
+ ```python
110
+ # Vacation mode check
111
+ if mode_id == DhwOperationSetting.VACATION.value:
112
+ # handle vacation days parameter
113
+ ```
114
+
115
+ ## Value Mappings
116
+
117
+ ### DhwOperationSetting (User Preferences)
118
+ ```python
119
+ DhwOperationSetting.HEAT_PUMP = 1 # Heat Pump Only
120
+ DhwOperationSetting.ELECTRIC = 2 # Electric Only
121
+ DhwOperationSetting.ENERGY_SAVER = 3 # Energy Saver (default)
122
+ DhwOperationSetting.HIGH_DEMAND = 4 # High Demand
123
+ DhwOperationSetting.VACATION = 5 # Vacation Mode
124
+ DhwOperationSetting.POWER_OFF = 6 # Device Powered Off
125
+ ```
126
+
127
+ ### CurrentOperationMode (Real-time States)
128
+ ```python
129
+ CurrentOperationMode.STANDBY = 0 # Device idle
130
+ CurrentOperationMode.HEAT_PUMP_MODE = 32 # Heat pump active
131
+ CurrentOperationMode.HYBRID_EFFICIENCY_MODE = 64 # Energy Saver mode heating
132
+ CurrentOperationMode.HYBRID_BOOST_MODE = 96 # High Demand mode heating
133
+ ```
134
+
135
+ ## Common Migration Patterns
136
+
137
+ ### Event Handlers
138
+ **Before:**
139
+ ```python
140
+ def on_mode_change(old_mode: OperationMode, new_mode: OperationMode):
141
+ if new_mode == OperationMode.HEAT_PUMP_MODE:
142
+ print("Heat pump started")
143
+ ```
144
+
145
+ **After:**
146
+ ```python
147
+ def on_mode_change(old_mode: CurrentOperationMode, new_mode: CurrentOperationMode):
148
+ if new_mode == CurrentOperationMode.HEAT_PUMP_MODE:
149
+ print("Heat pump started")
150
+ ```
151
+
152
+ ### Status Display Logic
153
+ **Before:**
154
+ ```python
155
+ def get_device_status(status: DeviceStatus) -> dict:
156
+ return {
157
+ 'mode': status.dhwOperationSetting.name, # User's setting
158
+ 'active': status.operationMode != OperationMode.STANDBY, # Currently heating?
159
+ 'powered_on': status.dhwOperationSetting != OperationMode.POWER_OFF
160
+ }
161
+ ```
162
+
163
+ **After:**
164
+ ```python
165
+ def get_device_status(status: DeviceStatus) -> dict:
166
+ return {
167
+ 'mode': status.dhwOperationSetting.name, # User's setting
168
+ 'active': status.operationMode != CurrentOperationMode.STANDBY, # Currently heating?
169
+ 'powered_on': status.dhwOperationSetting != DhwOperationSetting.POWER_OFF
170
+ }
171
+ ```
172
+
173
+ ## Backward Compatibility
174
+
175
+ The original `OperationMode` enum remains available for backward compatibility but is **deprecated**:
176
+
177
+ ```python
178
+ # Still works, but deprecated
179
+ from nwp500 import OperationMode
180
+ if status.dhwOperationSetting == OperationMode.ENERGY_SAVER:
181
+ pass
182
+ ```
183
+
184
+ **Note**: The deprecated enum will be removed in a future major version (3.0+).
185
+
186
+ ## Testing Your Migration
187
+
188
+ Use the migration helper to validate your changes:
189
+
190
+ ```python
191
+ from nwp500 import migrate_operation_mode_usage
192
+
193
+ # Get migration guidance
194
+ guide = migrate_operation_mode_usage()
195
+ print(guide['migration_examples'])
196
+ ```
197
+
198
+ ## Timeline
199
+
200
+ - **Current (2.x)**: Both old and new enums available, old enum deprecated
201
+ - **Next minor (2.x+1)**: Deprecation warnings added
202
+ - **Future major (3.0)**: `OperationMode` enum removed
203
+
204
+ ## Need Help?
205
+
206
+ - Check the [documentation](docs/DEVICE_STATUS_FIELDS.rst) for detailed explanations
207
+ - Use the `migrate_operation_mode_usage()` helper function for guidance
208
+ - Look at updated examples in the `examples/` directory
209
+ - File an issue if you encounter migration problems
210
+
211
+ ## Benefits After Migration
212
+
213
+ 1. **Type Safety**: Compiler/IDE catches enum mismatches
214
+ 2. **Clarity**: Clear distinction between user preferences and device state
215
+ 3. **Better Documentation**: Each enum has focused, specific documentation
216
+ 4. **Future-Proof**: Easier to extend either enum independently
217
+ 5. **Reduced Bugs**: Impossible to accidentally compare incompatible concepts
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nwp500-python
3
- Version: 1.2.3
3
+ Version: 2.0.0
4
4
  Summary: A library for controlling Navien NWP500 Water Heaters via NaviLink
5
5
  Home-page: https://github.com/eman/nwp500-python
6
6
  Author: Emmanuel Levijarvi
@@ -44,7 +44,7 @@ This document lists the fields found in the ``status`` object of device status m
44
44
  - Sub error code providing additional error details. See ERROR_CODES.rst for details.
45
45
  - None
46
46
  * - ``operationMode``
47
- - OperationMode
47
+ - CurrentOperationMode
48
48
  - None
49
49
  - The current **actual operational state** of the device (what it's doing RIGHT NOW). Reports status values: 0=Standby, 32=Heat Pump active, 64=Energy Saver active, 96=High Demand active. See Operation Modes section below for the critical distinction between this and ``dhwOperationSetting``.
50
50
  - None
@@ -229,9 +229,9 @@ This document lists the fields found in the ``status`` object of device status m
229
229
  - Type of program reservation.
230
230
  - None
231
231
  * - ``dhwOperationSetting``
232
- - OperationMode
232
+ - DhwOperationSetting
233
233
  - None
234
- - User's configured DHW operation mode preference. This field uses the same ``OperationMode`` enum as ``operationMode`` but contains command mode values (1=HEAT_PUMP, 2=ELECTRIC, 3=ENERGY_SAVER, 4=HIGH_DEMAND, 5=VACATION, 6=POWER_OFF). When the device is powered off via the power-off command, this field will show 6 (POWER_OFF). This is how to distinguish between "powered off" vs "on but in standby". See the Operation Modes section below for details.
234
+ - User's configured DHW operation mode preference. This field uses the ``DhwOperationSetting`` enum (separate from ``CurrentOperationMode``) and contains command mode values (1=HEAT_PUMP, 2=ELECTRIC, 3=ENERGY_SAVER, 4=HIGH_DEMAND, 5=VACATION, 6=POWER_OFF). When the device is powered off via the power-off command, this field will show 6 (POWER_OFF). This is how to distinguish between "powered off" vs "on but in standby". See the Operation Modes section below for details.
235
235
  - None
236
236
  * - ``temperatureType``
237
237
  - integer
@@ -533,10 +533,10 @@ These two fields serve different purposes and it's critical to understand their
533
533
  Field Definitions
534
534
  ^^^^^^^^^^^^^^^^^
535
535
 
536
- **dhwOperationSetting** (OperationMode enum with command values 1-5)
536
+ **dhwOperationSetting** (DhwOperationSetting enum with command values 1-6)
537
537
  The user's **configured mode preference** - what heating mode the device should use when it needs to heat water. This is set via the ``dhw-mode`` command and persists until changed by the user or device.
538
538
 
539
- * Type: ``OperationMode`` enum
539
+ * Type: ``DhwOperationSetting`` enum
540
540
  * Values:
541
541
 
542
542
  * 1 = ``HEAT_PUMP`` (Heat Pump Only)
@@ -551,10 +551,10 @@ Field Definitions
551
551
  * Meaning: "When heating is needed, use this mode" OR "I'm powered off" (if value is 6)
552
552
  * Value 6 (``POWER_OFF``) indicates the device was powered off via the power-off command. This is how to distinguish between "powered off" and "on but idle".
553
553
 
554
- **operationMode** (OperationMode enum with status values 0, 32, 64, 96)
554
+ **operationMode** (CurrentOperationMode enum with status values 0, 32, 64, 96)
555
555
  The device's **current actual operational state** - what the device is doing RIGHT NOW. This reflects real-time operation and changes automatically based on whether the device is idle or actively heating.
556
556
 
557
- * Type: ``OperationMode`` enum
557
+ * Type: ``CurrentOperationMode`` enum
558
558
  * Values:
559
559
 
560
560
  * 0 = ``STANDBY`` (Idle, not heating)
@@ -661,11 +661,13 @@ For user-facing applications, follow these guidelines:
661
661
  **Code Example**
662
662
  .. code-block:: python
663
663
 
664
+ from nwp500.models import DeviceStatus, DhwOperationSetting, CurrentOperationMode
665
+
664
666
  def format_mode_display(status: DeviceStatus) -> dict:
665
667
  """Format mode and status for UI display."""
666
668
 
667
669
  # Check if device is powered off first
668
- if status.dhwOperationSetting == OperationMode.POWER_OFF:
670
+ if status.dhwOperationSetting == DhwOperationSetting.POWER_OFF:
669
671
  return {
670
672
  'configured_mode': 'Off',
671
673
  'operational_state': 'Powered Off',
@@ -678,16 +680,16 @@ For user-facing applications, follow these guidelines:
678
680
  configured_mode = status.dhwOperationSetting.name.replace('_', ' ').title()
679
681
 
680
682
  # Current operational state
681
- if status.operationMode == OperationMode.STANDBY:
683
+ if status.operationMode == CurrentOperationMode.STANDBY:
682
684
  operational_state = "Idle"
683
685
  is_heating = False
684
- elif status.operationMode == OperationMode.HEAT_PUMP_MODE:
686
+ elif status.operationMode == CurrentOperationMode.HEAT_PUMP_MODE:
685
687
  operational_state = "Heating (Heat Pump)"
686
688
  is_heating = True
687
- elif status.operationMode == OperationMode.HYBRID_EFFICIENCY_MODE:
689
+ elif status.operationMode == CurrentOperationMode.HYBRID_EFFICIENCY_MODE:
688
690
  operational_state = "Heating (Energy Saver)"
689
691
  is_heating = True
690
- elif status.operationMode == OperationMode.HYBRID_BOOST_MODE:
692
+ elif status.operationMode == CurrentOperationMode.HYBRID_BOOST_MODE:
691
693
  operational_state = "Heating (High Demand)"
692
694
  is_heating = True
693
695
  else:
@@ -712,9 +714,9 @@ For user-facing applications, follow these guidelines:
712
714
 
713
715
  4. **operationMode changes automatically** - you cannot directly set this; it changes based on device operation
714
716
 
715
- 5. **Both fields use OperationMode enum** - but different value ranges (1-6 for dhwOperationSetting, 0/32/64/96 for operationMode)
717
+ 5. **Separate enum types provide clarity** - ``DhwOperationSetting`` (values 1-6) for user preferences, ``CurrentOperationMode`` (values 0/32/64/96) for real-time states
716
718
 
717
- 6. **Power off detection** - Check if ``dhwOperationSetting == 6`` (``POWER_OFF``) to determine if device is powered off vs just idle
719
+ 6. **Power off detection** - Check if ``dhwOperationSetting == DhwOperationSetting.POWER_OFF`` to determine if device is powered off vs just idle
718
720
 
719
721
  Technical Notes
720
722
  ---------------
@@ -29,7 +29,7 @@ logging.basicConfig(
29
29
  )
30
30
 
31
31
  from nwp500 import NavienAPIClient, NavienAuthClient, NavienMqttClient
32
- from nwp500.models import DeviceStatus, OperationMode
32
+ from nwp500.models import DeviceStatus, CurrentOperationMode
33
33
 
34
34
 
35
35
  # Example 1: Multiple listeners for the same event
@@ -52,16 +52,18 @@ async def save_temperature_to_db(old_temp: float, new_temp: float):
52
52
 
53
53
 
54
54
  # Example 2: Mode change handlers
55
- def log_mode_change(old_mode: OperationMode, new_mode: OperationMode):
55
+ def log_mode_change(old_mode: CurrentOperationMode, new_mode: CurrentOperationMode):
56
56
  """Log operation mode changes."""
57
57
  print(f"🔄 [Mode] Changed from {old_mode.name} to {new_mode.name}")
58
58
 
59
59
 
60
- def optimize_on_mode_change(old_mode: OperationMode, new_mode: OperationMode):
60
+ def optimize_on_mode_change(
61
+ old_mode: CurrentOperationMode, new_mode: CurrentOperationMode
62
+ ):
61
63
  """Optimization handler."""
62
- if new_mode == OperationMode.HEAT_PUMP_ONLY:
64
+ if new_mode == CurrentOperationMode.HEAT_PUMP_MODE:
63
65
  print("♻️ [Optimizer] Heat pump mode - maximum efficiency!")
64
- elif new_mode == OperationMode.HIGH_DEMAND:
66
+ elif new_mode == CurrentOperationMode.HYBRID_BOOST_MODE:
65
67
  print("⚡ [Optimizer] High demand mode - fast recovery!")
66
68
 
67
69
 
@@ -75,7 +75,7 @@ async def main():
75
75
  print(f"\n--- Status Response #{status_count} ---")
76
76
  print(f" Temperature: {status.dhwTemperature:.1f}°F")
77
77
  print(f" Power: {status.currentInstPower:.1f}W")
78
- print(f" Available Energy: {status.availableEnergyCapacity:.1f}%")
78
+ print(f" Available Energy: {status.availableEnergyCapacity:.0f} Wh")
79
79
 
80
80
  def on_device_feature(feature: DeviceFeature):
81
81
  """Callback receives parsed DeviceFeature objects."""
@@ -17,8 +17,13 @@ async def _wait_for_controller_serial(mqtt_client: NavienMqttClient, device) ->
17
17
  if not feature_future.done():
18
18
  feature_future.set_result(feature)
19
19
 
20
- mqtt_client.once("feature_received", capture_feature)
20
+ # Subscribe to device feature messages first
21
+ await mqtt_client.subscribe_device_feature(device, capture_feature)
22
+
23
+ # Then request device info
21
24
  await mqtt_client.request_device_info(device)
25
+
26
+ # Wait for the response
22
27
  feature = await asyncio.wait_for(feature_future, timeout=15)
23
28
  return feature.controllerSerialNumber
24
29