nwp500-python 1.0.3__tar.gz → 1.1.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 (91) hide show
  1. {nwp500_python-1.0.3/src/nwp500_python.egg-info → nwp500_python-1.1.0}/PKG-INFO +33 -7
  2. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/README.rst +32 -6
  3. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/DEVICE_STATUS_FIELDS.rst +208 -17
  4. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/MQTT_CLIENT.rst +14 -5
  5. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/MQTT_MESSAGES.rst +1 -1
  6. nwp500_python-1.1.0/examples/power_control_example.py +137 -0
  7. nwp500_python-1.1.0/examples/set_dhw_temperature_example.py +120 -0
  8. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/src/nwp500/cli.py +165 -7
  9. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/src/nwp500/models.py +22 -5
  10. {nwp500_python-1.0.3 → nwp500_python-1.1.0/src/nwp500_python.egg-info}/PKG-INFO +33 -7
  11. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/src/nwp500_python.egg-info/SOURCES.txt +2 -0
  12. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/.coveragerc +0 -0
  13. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/.github/copilot-instructions.md +0 -0
  14. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/.github/workflows/ci.yml +0 -0
  15. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/.github/workflows/release.yml +0 -0
  16. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/.gitignore +0 -0
  17. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/.pre-commit-config.yaml +0 -0
  18. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/.readthedocs.yml +0 -0
  19. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/AUTHORS.rst +0 -0
  20. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/CHANGELOG.rst +0 -0
  21. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/CONTRIBUTING.rst +0 -0
  22. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/LICENSE.txt +0 -0
  23. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/Makefile +0 -0
  24. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/RELEASE.md +0 -0
  25. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/API_CLIENT.rst +0 -0
  26. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/API_REFERENCE.rst +0 -0
  27. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/AUTHENTICATION.rst +0 -0
  28. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/COMMAND_QUEUE.rst +0 -0
  29. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/DEVELOPMENT.rst +0 -0
  30. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/DEVICE_FEATURE_FIELDS.rst +0 -0
  31. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/ENERGY_MONITORING.rst +0 -0
  32. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/ERROR_CODES.rst +0 -0
  33. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/EVENT_EMITTER.rst +0 -0
  34. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/Makefile +0 -0
  35. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/_static/.gitignore +0 -0
  36. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/authors.rst +0 -0
  37. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/changelog.rst +0 -0
  38. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/conf.py +0 -0
  39. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/contributing.rst +0 -0
  40. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/index.rst +0 -0
  41. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/license.rst +0 -0
  42. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/openapi.yaml +0 -0
  43. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/readme.rst +0 -0
  44. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/docs/requirements.txt +0 -0
  45. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/.ruff.toml +0 -0
  46. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/README.md +0 -0
  47. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/api_client_example.py +0 -0
  48. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/auth_constructor_example.py +0 -0
  49. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/authenticate.py +0 -0
  50. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/combined_callbacks.py +0 -0
  51. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/command_queue_demo.py +0 -0
  52. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/device_feature_callback.py +0 -0
  53. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/device_status_callback.py +0 -0
  54. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/device_status_callback_debug.py +0 -0
  55. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/energy_usage_example.py +0 -0
  56. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/event_emitter_demo.py +0 -0
  57. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/improved_auth_pattern.py +0 -0
  58. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/mask.py +0 -0
  59. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/mqtt_client_example.py +0 -0
  60. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/periodic_device_info.py +0 -0
  61. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/periodic_requests.py +0 -0
  62. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/reconnection_demo.py +0 -0
  63. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/set_mode_example.py +0 -0
  64. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/simple_periodic_info.py +0 -0
  65. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/simple_periodic_status.py +0 -0
  66. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/test_api_client.py +0 -0
  67. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/test_mqtt_connection.py +0 -0
  68. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/test_mqtt_messaging.py +0 -0
  69. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/examples/test_periodic_minimal.py +0 -0
  70. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/pyproject.toml +0 -0
  71. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/scripts/format.py +0 -0
  72. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/scripts/lint.py +0 -0
  73. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/scripts/setup-dev.py +0 -0
  74. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/setup.cfg +0 -0
  75. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/setup.py +0 -0
  76. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/src/nwp500/__init__.py +0 -0
  77. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/src/nwp500/api_client.py +0 -0
  78. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/src/nwp500/auth.py +0 -0
  79. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/src/nwp500/config.py +0 -0
  80. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/src/nwp500/constants.py +0 -0
  81. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/src/nwp500/events.py +0 -0
  82. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/src/nwp500/mqtt_client.py +0 -0
  83. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/src/nwp500_python.egg-info/dependency_links.txt +0 -0
  84. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/src/nwp500_python.egg-info/entry_points.txt +0 -0
  85. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/src/nwp500_python.egg-info/not-zip-safe +0 -0
  86. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/src/nwp500_python.egg-info/requires.txt +0 -0
  87. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/src/nwp500_python.egg-info/top_level.txt +0 -0
  88. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/tests/conftest.py +0 -0
  89. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/tests/test_command_queue.py +0 -0
  90. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/tests/test_events.py +0 -0
  91. {nwp500_python-1.0.3 → nwp500_python-1.1.0}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nwp500-python
3
- Version: 1.0.3
3
+ Version: 1.1.0
4
4
  Summary: Add a short description here!
5
5
  Home-page: https://github.com/pyscaffold/pyscaffold/
6
6
  Author: Emmanuel Levijarvi
@@ -116,9 +116,30 @@ The library includes a command line interface for quick monitoring and device in
116
116
  # Get device feature/capability information
117
117
  python -m nwp500.cli --device-feature
118
118
 
119
+ # Turn device on
120
+ python -m nwp500.cli --power-on
121
+
122
+ # Turn device off
123
+ python -m nwp500.cli --power-off
124
+
125
+ # Turn device on and see updated status
126
+ python -m nwp500.cli --power-on --status
127
+
119
128
  # Set operation mode and see response
120
129
  python -m nwp500.cli --set-mode energy-saver
121
130
 
131
+ # Set DHW target temperature and see response
132
+ python -m nwp500.cli --set-dhw-temp 140
133
+
134
+ # Set temperature and then get updated status
135
+ python -m nwp500.cli --set-dhw-temp 140 --status
136
+
137
+ # Set mode and then get updated status
138
+ python -m nwp500.cli --set-mode energy-saver --status
139
+
140
+ # Just get current status (one-time)
141
+ python -m nwp500.cli --status
142
+
122
143
  # Monitor continuously (default - writes to CSV)
123
144
  python -m nwp500.cli --monitor
124
145
 
@@ -127,10 +148,13 @@ The library includes a command line interface for quick monitoring and device in
127
148
 
128
149
  **Available CLI Options:**
129
150
 
130
- * ``--status``: Print current device status as JSON and exit
151
+ * ``--status``: Print current device status as JSON. Can be combined with control commands to see updated status.
131
152
  * ``--device-info``: Print comprehensive device information (firmware, model, capabilities) via MQTT as JSON and exit
132
153
  * ``--device-feature``: Print device capabilities and feature settings via MQTT as JSON and exit
154
+ * ``--power-on``: Turn the device on and display response
155
+ * ``--power-off``: Turn the device off and display response
133
156
  * ``--set-mode MODE``: Set operation mode and display response. Valid modes: heat-pump, energy-saver, high-demand, electric, vacation, standby
157
+ * ``--set-dhw-temp TEMP``: Set DHW (Domestic Hot Water) target temperature in Fahrenheit (115-150°F) and display response
134
158
  * ``--monitor``: Continuously monitor status every 30 seconds and log to CSV (default)
135
159
  * ``-o, --output``: Specify CSV output filename for monitoring mode
136
160
  * ``--email``: Override email (alternative to environment variable)
@@ -180,19 +204,21 @@ Operation Modes
180
204
  * - Heat Pump Mode
181
205
  - 1
182
206
  - Most energy-efficient mode using only the heat pump. Longest recovery time.
183
- * - Energy Saver Mode
207
+ * - Electric Mode
184
208
  - 2
209
+ - Fastest recovery using only electric heaters. Least energy-efficient.
210
+ * - Energy Saver Mode
211
+ - 3
185
212
  - Default mode. Balances efficiency and recovery time using both heat pump and electric heater.
186
213
  * - High Demand Mode
187
- - 3
188
- - Uses electric heater more frequently for faster recovery time.
189
- * - Electric Mode
190
214
  - 4
191
- - Fastest recovery using only electric heaters. Least energy-efficient.
215
+ - Uses electric heater more frequently for faster recovery time.
192
216
  * - Vacation Mode
193
217
  - 5
194
218
  - Suspends heating to save energy during extended absences.
195
219
 
220
+ **Important:** When you set a mode, you're configuring the ``dhwOperationSetting`` (what mode to use when heating). The device's current operational state is reported in ``operationMode`` (0=Standby, 32=Heat Pump active, 64=Energy Saver active, 96=High Demand active). See the `Device Status Fields documentation <docs/DEVICE_STATUS_FIELDS.rst>`_ for details on this distinction.
221
+
196
222
  MQTT Protocol
197
223
  =============
198
224
 
@@ -79,9 +79,30 @@ The library includes a command line interface for quick monitoring and device in
79
79
  # Get device feature/capability information
80
80
  python -m nwp500.cli --device-feature
81
81
 
82
+ # Turn device on
83
+ python -m nwp500.cli --power-on
84
+
85
+ # Turn device off
86
+ python -m nwp500.cli --power-off
87
+
88
+ # Turn device on and see updated status
89
+ python -m nwp500.cli --power-on --status
90
+
82
91
  # Set operation mode and see response
83
92
  python -m nwp500.cli --set-mode energy-saver
84
93
 
94
+ # Set DHW target temperature and see response
95
+ python -m nwp500.cli --set-dhw-temp 140
96
+
97
+ # Set temperature and then get updated status
98
+ python -m nwp500.cli --set-dhw-temp 140 --status
99
+
100
+ # Set mode and then get updated status
101
+ python -m nwp500.cli --set-mode energy-saver --status
102
+
103
+ # Just get current status (one-time)
104
+ python -m nwp500.cli --status
105
+
85
106
  # Monitor continuously (default - writes to CSV)
86
107
  python -m nwp500.cli --monitor
87
108
 
@@ -90,10 +111,13 @@ The library includes a command line interface for quick monitoring and device in
90
111
 
91
112
  **Available CLI Options:**
92
113
 
93
- * ``--status``: Print current device status as JSON and exit
114
+ * ``--status``: Print current device status as JSON. Can be combined with control commands to see updated status.
94
115
  * ``--device-info``: Print comprehensive device information (firmware, model, capabilities) via MQTT as JSON and exit
95
116
  * ``--device-feature``: Print device capabilities and feature settings via MQTT as JSON and exit
117
+ * ``--power-on``: Turn the device on and display response
118
+ * ``--power-off``: Turn the device off and display response
96
119
  * ``--set-mode MODE``: Set operation mode and display response. Valid modes: heat-pump, energy-saver, high-demand, electric, vacation, standby
120
+ * ``--set-dhw-temp TEMP``: Set DHW (Domestic Hot Water) target temperature in Fahrenheit (115-150°F) and display response
97
121
  * ``--monitor``: Continuously monitor status every 30 seconds and log to CSV (default)
98
122
  * ``-o, --output``: Specify CSV output filename for monitoring mode
99
123
  * ``--email``: Override email (alternative to environment variable)
@@ -143,19 +167,21 @@ Operation Modes
143
167
  * - Heat Pump Mode
144
168
  - 1
145
169
  - Most energy-efficient mode using only the heat pump. Longest recovery time.
146
- * - Energy Saver Mode
170
+ * - Electric Mode
147
171
  - 2
172
+ - Fastest recovery using only electric heaters. Least energy-efficient.
173
+ * - Energy Saver Mode
174
+ - 3
148
175
  - Default mode. Balances efficiency and recovery time using both heat pump and electric heater.
149
176
  * - High Demand Mode
150
- - 3
151
- - Uses electric heater more frequently for faster recovery time.
152
- * - Electric Mode
153
177
  - 4
154
- - Fastest recovery using only electric heaters. Least energy-efficient.
178
+ - Uses electric heater more frequently for faster recovery time.
155
179
  * - Vacation Mode
156
180
  - 5
157
181
  - Suspends heating to save energy during extended absences.
158
182
 
183
+ **Important:** When you set a mode, you're configuring the ``dhwOperationSetting`` (what mode to use when heating). The device's current operational state is reported in ``operationMode`` (0=Standby, 32=Heat Pump active, 64=Energy Saver active, 96=High Demand active). See the `Device Status Fields documentation <docs/DEVICE_STATUS_FIELDS.rst>`_ for details on this distinction.
184
+
159
185
  MQTT Protocol
160
186
  =============
161
187
 
@@ -44,9 +44,9 @@ 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
- - integer
47
+ - OperationMode
48
48
  - None
49
- - The current operation mode of the device. See Operation Modes section below.
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
51
51
  * - ``operationBusy``
52
52
  - integer
@@ -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
- - integer
232
+ - OperationMode
233
233
  - None
234
- - DHW operation setting.
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.
235
235
  - None
236
236
  * - ``temperatureType``
237
237
  - integer
@@ -469,20 +469,20 @@ The ``operationMode`` field is an integer that maps to the following modes. Thes
469
469
  - High
470
470
  - Most energy-efficient mode, using only the heat pump. Recovery time varies with ambient temperature and humidity. Higher ambient temperature and humidity improve efficiency and reduce recovery time.
471
471
  * - 2
472
+ - Electric
473
+ - Fast
474
+ - Very Low
475
+ - Uses only upper and lower electric heaters (not simultaneously). Least energy-efficient with shortest recovery time. Can operate continuously for up to 72 hours before automatically reverting to previous mode.
476
+ * - 3
472
477
  - Energy Saver (Hybrid: Efficiency)
473
478
  - Fast
474
479
  - Very High
475
480
  - Default mode. Combines the heat pump and electric heater for balanced efficiency and recovery time. Heat pump is primarily used with electric heater for backup. Applied during initial shipment and factory reset.
476
- * - 3
481
+ * - 4
477
482
  - High Demand (Hybrid: Boost)
478
483
  - Very Fast
479
484
  - Low
480
485
  - Combines heat pump and electric heater with more frequent use of electric heater for faster recovery. Suitable when higher hot water supply is needed.
481
- * - 4
482
- - Electric
483
- - Fast
484
- - Very Low
485
- - Uses only upper and lower electric heaters (not simultaneously). Least energy-efficient with shortest recovery time. Can operate continuously for up to 72 hours before automatically reverting to previous mode.
486
486
  * - 5
487
487
  - Vacation
488
488
  - None
@@ -490,10 +490,10 @@ The ``operationMode`` field is an integer that maps to the following modes. Thes
490
490
  - Suspends heating to save energy during absences (0-99 days). Only minimal operations like freeze protection and anti-seize are performed. Heating resumes 9 hours before the vacation period ends.
491
491
 
492
492
 
493
- Observed Operation Modes (from network traffic analysis)
493
+ Operation Modes
494
494
  --------------------------------------------------------
495
495
 
496
- The following ``operationMode`` values have been observed in status messages from the device. These values appear to correspond to the commanded modes as follows:
496
+ The following ``operationMode`` values in status messages from the device. These values appear to correspond to the commanded modes as follows:
497
497
 
498
498
  .. list-table::
499
499
  :header-rows: 1
@@ -509,13 +509,204 @@ The following ``operationMode`` values have been observed in status messages fro
509
509
  - Heat Pump
510
510
  - Corresponds to commanded mode ``HEAT_PUMP`` (1).
511
511
  * - 64
512
- - Energy Saver
513
- - Corresponds to commanded mode ``ENERGY_SAVER`` (2).
512
+ - Energy Saver (Hybrid: Efficiency)
513
+ - Corresponds to commanded mode ``ENERGY_SAVER`` (3).
514
514
  * - 96
515
- - High Demand
516
- - Corresponds to commanded mode ``HIGH_DEMAND`` (3).
515
+ - High Demand (Hybrid: Boost)
516
+ - Corresponds to commanded mode ``HIGH_DEMAND`` (4).
517
+
518
+ The commanded mode ``ELECTRIC`` (2) has been observed to result in ``operationMode`` values of both 64 and 96 at different times.
519
+
520
+ Understanding operationMode vs dhwOperationSetting
521
+ ---------------------------------------------------
522
+
523
+ These two fields serve different purposes and it's critical to understand their relationship:
524
+
525
+ Field Definitions
526
+ ^^^^^^^^^^^^^^^^^
527
+
528
+ **dhwOperationSetting** (OperationMode enum with command values 1-5)
529
+ 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.
530
+
531
+ * Type: ``OperationMode`` enum
532
+ * Values:
533
+
534
+ * 1 = ``HEAT_PUMP`` (Heat Pump Only)
535
+ * 2 = ``ELECTRIC`` (Electric Only)
536
+ * 3 = ``ENERGY_SAVER`` (Hybrid: Efficiency)
537
+ * 4 = ``HIGH_DEMAND`` (Hybrid: Boost)
538
+ * 5 = ``VACATION`` (Vacation mode)
539
+ * 6 = ``POWER_OFF`` (Device is powered off)
540
+
541
+ * Set by: User via app, CLI, or MQTT command
542
+ * Changes: Only when user explicitly changes the mode or powers device off/on
543
+ * Meaning: "When heating is needed, use this mode" OR "I'm powered off" (if value is 6)
544
+ * **Critical**: 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".
545
+
546
+ **operationMode** (OperationMode enum with status values 0, 32, 64, 96)
547
+ 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.
548
+
549
+ * Type: ``OperationMode`` enum
550
+ * Values:
551
+
552
+ * 0 = ``STANDBY`` (Idle, not heating)
553
+ * 32 = ``HEAT_PUMP_MODE`` (Heat Pump actively running)
554
+ * 64 = ``HYBRID_EFFICIENCY_MODE`` (Energy Saver actively heating)
555
+ * 96 = ``HYBRID_BOOST_MODE`` (High Demand actively heating)
556
+
557
+ * Set by: Device automatically based on heating demand
558
+ * Changes: Dynamically as device starts/stops heating
559
+ * Meaning: "This is what I'm doing right now"
560
+ * **Note**: This field shows ``STANDBY`` (0) both when device is powered off AND when it's on but not heating. Check ``dhwOperationSetting`` to determine if device is actually powered off (value 6).
561
+
562
+ Key Relationship
563
+ ^^^^^^^^^^^^^^^^
564
+
565
+ The relationship between these fields can be summarized as:
566
+
567
+ * ``dhwOperationSetting`` = "What mode to use when heating"
568
+ * ``operationMode`` = "Am I heating right now, and if so, how?"
569
+
570
+ A device can be **idle** (``operationMode = STANDBY``) while still being **configured** for a specific heating mode (``dhwOperationSetting = ENERGY_SAVER``). When the tank temperature drops and heating begins, ``operationMode`` will change to reflect active heating (e.g., ``HYBRID_EFFICIENCY_MODE``), but ``dhwOperationSetting`` remains unchanged.
571
+
572
+ Real-World Examples
573
+ ^^^^^^^^^^^^^^^^^^^
574
+
575
+ **Example 1: Energy Saver Mode, Tank is Hot**
576
+ ::
577
+
578
+ dhwOperationSetting = 3 (ENERGY_SAVER) # Configured mode
579
+ operationMode = 0 (STANDBY) # Currently idle
580
+ dhwChargePer = 100 # Tank is fully charged
581
+
582
+ *Interpretation:* Device is configured for Energy Saver mode, but water is already at temperature so no heating is occurring.
583
+
584
+ **Example 2: Energy Saver Mode, Actively Heating**
585
+ ::
586
+
587
+ dhwOperationSetting = 3 (ENERGY_SAVER) # Configured mode
588
+ operationMode = 64 (HYBRID_EFFICIENCY_MODE) # Actively heating
589
+ operationBusy = true # Heating in progress
590
+ dhwChargePer = 75 # Tank at 75%
591
+
592
+ *Interpretation:* Device is using Energy Saver mode to heat the tank, currently at 75% charge.
593
+
594
+ **Example 3: High Demand Mode, Heat Pump Running**
595
+ ::
596
+
597
+ dhwOperationSetting = 4 (HIGH_DEMAND) # Configured mode
598
+ operationMode = 32 (HEAT_PUMP_MODE) # Heat pump active
599
+ compUse = true # Compressor running
600
+
601
+ *Interpretation:* Device is configured for High Demand but is currently running just the heat pump component (hybrid heating will engage electric elements as needed).
602
+
603
+ **Example 4: Device Powered Off**
604
+ ::
605
+
606
+ dhwOperationSetting = 6 (POWER_OFF) # Device powered off
607
+ operationMode = 0 (STANDBY) # Currently idle
608
+ operationBusy = false # No heating
609
+
610
+ *Interpretation:* Device was powered off using the power-off command. Although ``operationMode`` shows ``STANDBY`` (same as an idle device), the ``dhwOperationSetting`` value of 6 indicates it's actually powered off, not just idle.
611
+
612
+ Displaying Status in a User Interface
613
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
614
+
615
+ For user-facing applications, follow these guidelines:
616
+
617
+ **Primary Mode Display**
618
+ Use ``dhwOperationSetting`` to show the user's configured mode preference. This is what users expect to see as "the current mode" because it represents their selection.
619
+
620
+ **Important**: Check for value 6 (``POWER_OFF``) first to show "Off" or "Powered Off" status.
621
+
622
+ Example display::
623
+
624
+ Mode: Energy Saver [when dhwOperationSetting = 1-5]
625
+ Mode: Off [when dhwOperationSetting = 6]
626
+
627
+ **Status Indicator**
628
+ Use ``operationMode`` to show real-time operational status:
629
+
630
+ * ``STANDBY`` (0): Show "Idle" or "Standby" indicator (but check ``dhwOperationSetting`` for power-off state)
631
+ * ``HEAT_PUMP_MODE`` (32): Show "Heating (Heat Pump)" or heating indicator
632
+ * ``HYBRID_EFFICIENCY_MODE`` (64): Show "Heating (Energy Saver)" or heating indicator
633
+ * ``HYBRID_BOOST_MODE`` (96): Show "Heating (High Demand)" or heating indicator
634
+
635
+ **Combined Display Examples**
636
+ ::
637
+
638
+ # Device on and idle
639
+ Mode: Energy Saver
640
+ Status: Idle ○
641
+ Tank: 100%
642
+
643
+ # Device on and heating
644
+ Mode: Energy Saver
645
+ Status: Heating ●
646
+ Tank: 75%
647
+
648
+ # Device powered off
649
+ Mode: Off
650
+ Status: Powered Off
651
+ Tank: 100%
652
+
653
+ **Code Example**
654
+ .. code-block:: python
655
+
656
+ def format_mode_display(status: DeviceStatus) -> dict:
657
+ """Format mode and status for UI display."""
658
+
659
+ # Check if device is powered off first
660
+ if status.dhwOperationSetting == OperationMode.POWER_OFF:
661
+ return {
662
+ 'configured_mode': 'Off',
663
+ 'operational_state': 'Powered Off',
664
+ 'is_heating': False,
665
+ 'is_powered_on': False,
666
+ 'tank_charge': status.dhwChargePer,
667
+ }
668
+
669
+ # User's configured mode (what they selected)
670
+ configured_mode = status.dhwOperationSetting.name.replace('_', ' ').title()
671
+
672
+ # Current operational state
673
+ if status.operationMode == OperationMode.STANDBY:
674
+ operational_state = "Idle"
675
+ is_heating = False
676
+ elif status.operationMode == OperationMode.HEAT_PUMP_MODE:
677
+ operational_state = "Heating (Heat Pump)"
678
+ is_heating = True
679
+ elif status.operationMode == OperationMode.HYBRID_EFFICIENCY_MODE:
680
+ operational_state = "Heating (Energy Saver)"
681
+ is_heating = True
682
+ elif status.operationMode == OperationMode.HYBRID_BOOST_MODE:
683
+ operational_state = "Heating (High Demand)"
684
+ is_heating = True
685
+ else:
686
+ operational_state = "Unknown"
687
+ is_heating = False
688
+
689
+ return {
690
+ 'configured_mode': configured_mode, # "Energy Saver"
691
+ 'operational_state': operational_state, # "Idle" or "Heating..."
692
+ 'is_heating': is_heating, # True/False
693
+ 'is_powered_on': True, # Device is on
694
+ 'tank_charge': status.dhwChargePer, # 0-100
695
+ }
696
+
697
+ **Display Notes**
698
+
699
+ 1. **Never display operationMode as "the mode"** - users don't care that the device is in "HYBRID_EFFICIENCY_MODE", they want to know it's set to "Energy Saver"
700
+
701
+ 2. **Do use operationMode for heating indicators** - it tells you whether the device is actively heating right now
702
+
703
+ 3. **Mode changes affect dhwOperationSetting** - when a user changes the mode, you're setting ``dhwOperationSetting``
704
+
705
+ 4. **operationMode changes automatically** - you cannot directly set this; it changes based on device operation
706
+
707
+ 5. **Both fields use OperationMode enum** - but different value ranges (1-6 for dhwOperationSetting, 0/32/64/96 for operationMode)
517
708
 
518
- The commanded mode ``ELECTRIC`` (4) has been observed to result in ``operationMode`` values of both 64 and 96 at different times.
709
+ 6. **Power off detection** - Check if ``dhwOperationSetting == 6`` (``POWER_OFF``) to determine if device is powered off vs just idle
519
710
 
520
711
  Technical Notes
521
712
  ---------------
@@ -442,15 +442,21 @@ set_dhw_mode()
442
442
 
443
443
  await mqtt_client.set_dhw_mode(device: Device, mode_id: int) -> int
444
444
 
445
- Set DHW (Domestic Hot Water) operation mode.
445
+ Set DHW (Domestic Hot Water) operation mode. This sets the ``dhwOperationSetting`` field, which determines what heating mode the device will use when it needs to heat water.
446
446
 
447
447
  **Command:** ``33554433``
448
448
 
449
449
  **Mode:** ``dhw-mode``
450
450
 
451
- **Mode IDs:** - ``1``: Heat Pump (most efficient, longest recovery) -
452
- ``2``: Electric (least efficient, fastest recovery) - ``3``: Energy
453
- Saver (default, balanced) - ``4``: High Demand (faster recovery)
451
+ **Mode IDs (command values):**
452
+
453
+ * ``1``: Heat Pump Only (most efficient, longest recovery)
454
+ * ``2``: Electric Only (least efficient, fastest recovery)
455
+ * ``3``: Energy Saver (default, balanced - Hybrid: Efficiency)
456
+ * ``4``: High Demand (faster recovery - Hybrid: Boost)
457
+ * ``5``: Vacation (suspend heating for 0-99 days)
458
+
459
+ **Important:** Setting the mode updates ``dhwOperationSetting`` but does not immediately change ``operationMode``. The ``operationMode`` field reflects the device's current operational state and changes automatically when the device starts/stops heating. See :doc:`DEVICE_STATUS_FIELDS` for details on the relationship between these fields.
454
460
 
455
461
  set_dhw_temperature()
456
462
  '''''''''''''''''''''
@@ -749,13 +755,16 @@ Response Message
749
755
  "dhwTemperature": 120,
750
756
  "tankUpperTemperature": 115,
751
757
  "tankLowerTemperature": 110,
752
- "operationMode": 3,
758
+ "operationMode": 64,
759
+ "dhwOperationSetting": 3,
753
760
  "dhwUse": true,
754
761
  "compUse": false
755
762
  }
756
763
  }
757
764
  }
758
765
 
766
+ Note: ``operationMode`` shows the current operational state (64 = Energy Saver actively heating), while ``dhwOperationSetting`` shows the configured mode preference (3 = Energy Saver). See :doc:`DEVICE_STATUS_FIELDS` for the distinction between these fields.
767
+
759
768
  Error Handling
760
769
  --------------
761
770
 
@@ -133,7 +133,7 @@ The device sends a response to a control message on the ``responseTopic`` specif
133
133
 
134
134
  The ``sessionID`` in the response corresponds to the ``sessionID`` of the request.
135
135
 
136
- The ``response`` object contains a ``status`` object that reflects the new state. For example, after a ``dhw-mode`` command with ``param`` ``[3]``\ , the ``dhwOperationSetting`` in the ``status`` object of the response will be ``3``.
136
+ The ``response`` object contains a ``status`` object that reflects the new state. For example, after a ``dhw-mode`` command with ``param`` ``[3]`` (Energy Saver), the ``dhwOperationSetting`` field in the ``status`` object will be ``3``. Note that ``operationMode`` may still show ``0`` (STANDBY) if the device is not currently heating. See :doc:`DEVICE_STATUS_FIELDS` for the important distinction between ``dhwOperationSetting`` (configured mode) and ``operationMode`` (current operational state).
137
137
 
138
138
  Device Status Messages
139
139
  ----------------------
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Example: Controlling device power via MQTT.
4
+
5
+ This demonstrates how to programmatically turn the water heater on and off
6
+ and receive confirmation of the power state change.
7
+ """
8
+
9
+ import asyncio
10
+ import logging
11
+ from nwp500 import NavienAuthClient, NavienAPIClient, NavienMqttClient
12
+
13
+ # Set up logging to see the power control process
14
+ logging.basicConfig(level=logging.INFO)
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ async def power_control_example():
19
+ """Example of controlling device power programmatically."""
20
+
21
+ # Use environment variables or replace with your credentials
22
+ email = "your_email@example.com"
23
+ password = "your_password"
24
+
25
+ async with NavienAuthClient(email, password) as auth_client:
26
+ # Get device information
27
+ api_client = NavienAPIClient(auth_client)
28
+ devices = await api_client.list_devices()
29
+
30
+ if not devices:
31
+ logger.error("No devices found")
32
+ return
33
+
34
+ device = devices[0]
35
+ logger.info(f"Found device: {device.device_info.device_name}")
36
+
37
+ # Connect MQTT client
38
+ mqtt_client = NavienMqttClient(auth_client)
39
+ await mqtt_client.connect()
40
+ logger.info("MQTT client connected")
41
+
42
+ try:
43
+ # Get current status first
44
+ logger.info("Getting current device status...")
45
+ current_status = None
46
+
47
+ def on_current_status(status):
48
+ nonlocal current_status
49
+ current_status = status
50
+ logger.info(f"Current operation mode: {status.operationMode.name}")
51
+ logger.info(f"Current DHW temperature: {status.dhwTemperature}°F")
52
+
53
+ await mqtt_client.subscribe_device_status(device, on_current_status)
54
+ await mqtt_client.request_device_status(device)
55
+ await asyncio.sleep(3) # Wait for current status
56
+
57
+ # Turn device off
58
+ logger.info("Turning device OFF...")
59
+
60
+ power_off_complete = False
61
+
62
+ def on_power_off_response(status):
63
+ nonlocal power_off_complete
64
+ logger.info("Power OFF response received!")
65
+ logger.info(f"Operation mode: {status.operationMode.name}")
66
+ logger.info(f"DHW Operation Setting: {status.dhwOperationSetting.name}")
67
+ power_off_complete = True
68
+
69
+ await mqtt_client.subscribe_device_status(device, on_power_off_response)
70
+ await mqtt_client.set_power(device, power_on=False)
71
+
72
+ # Wait for confirmation
73
+ for i in range(15): # Wait up to 15 seconds
74
+ if power_off_complete:
75
+ logger.info("Device turned OFF successfully!")
76
+ break
77
+ await asyncio.sleep(1)
78
+ else:
79
+ logger.warning("Timeout waiting for power OFF confirmation")
80
+
81
+ # Wait a bit before turning back on
82
+ logger.info("Waiting 5 seconds before turning device back ON...")
83
+ await asyncio.sleep(5)
84
+
85
+ # Turn device back on
86
+ logger.info("Turning device ON...")
87
+
88
+ power_on_complete = False
89
+
90
+ def on_power_on_response(status):
91
+ nonlocal power_on_complete
92
+ logger.info("Power ON response received!")
93
+ logger.info(f"Operation mode: {status.operationMode.name}")
94
+ logger.info(f"DHW Operation Setting: {status.dhwOperationSetting.name}")
95
+ logger.info(f"Tank charge: {status.dhwChargePer}%")
96
+ power_on_complete = True
97
+
98
+ await mqtt_client.subscribe_device_status(device, on_power_on_response)
99
+ await mqtt_client.set_power(device, power_on=True)
100
+
101
+ # Wait for confirmation
102
+ for i in range(15): # Wait up to 15 seconds
103
+ if power_on_complete:
104
+ logger.info("Device turned ON successfully!")
105
+ break
106
+ await asyncio.sleep(1)
107
+ else:
108
+ logger.warning("Timeout waiting for power ON confirmation")
109
+
110
+ finally:
111
+ await mqtt_client.disconnect()
112
+ logger.info("Disconnected from MQTT")
113
+
114
+
115
+ if __name__ == "__main__":
116
+ print("=== Power Control Example ===")
117
+ print("This example demonstrates:")
118
+ print("1. Connecting to device via MQTT")
119
+ print("2. Getting current device status")
120
+ print("3. Turning device OFF")
121
+ print("4. Turning device ON")
122
+ print("5. Receiving and displaying the responses")
123
+ print()
124
+
125
+ # Note: This requires valid credentials
126
+ print(
127
+ "Note: Update email/password or set NAVIEN_EMAIL/NAVIEN_PASSWORD environment variables"
128
+ )
129
+ print()
130
+
131
+ # Uncomment to run (requires valid credentials)
132
+ # asyncio.run(power_control_example())
133
+
134
+ print("CLI equivalent commands:")
135
+ print(" python -m nwp500.cli --power-off")
136
+ print(" python -m nwp500.cli --power-on")
137
+ print(" python -m nwp500.cli --power-on --status")