Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0cc20e1
feat: Add device capability system and advanced control commands
eman Dec 20, 2025
41e4192
fix: Remove MAC addresses from debug/error logging
eman Dec 20, 2025
2911ac0
chore: Exclude diagnostics_output directory from git
eman Dec 20, 2025
ca8dec6
chore: Remove mqtt_diagnostics_output directory and exclude from git
eman Dec 20, 2025
23927ae
test: Add comprehensive test coverage for new device capabilities fea…
eman Dec 20, 2025
72de760
style: Fix linting and line length issues in test files
eman Dec 20, 2025
49c5a17
refactor: Address review comments and security findings
eman Dec 20, 2025
f0edcca
refactor: Make ensure_device_info_cached public API
eman Dec 20, 2025
e27b142
fix: Enforce maximum value validation for vacation days
eman Dec 20, 2025
55e6650
fix: Use implicit string concatenation for error message
eman Dec 20, 2025
42dd31d
chore: Confirm all PR review comments addressed
eman Dec 20, 2025
035e42a
Refactor: Centralize MQTT control via .control, standardize periodic …
eman Dec 20, 2025
4bb6562
Security: Mask sensitive data in logs and refactor CLI for maintainab…
eman Dec 20, 2025
3a95ca8
fix: Resolve CI errors, lint violations, and test failures
eman Dec 20, 2025
1576be2
Fix CI linting errors: line length and import sorting
eman Dec 20, 2025
6045932
fix: Remove sensitive token logging from docstring examples
eman Dec 20, 2025
65eefd3
fix: Remove MAC address from sensitive data logging in command_decora…
eman Dec 20, 2025
d7ab9c0
fix: Remove MAC address from sensitive data logging in mqtt_client
eman Dec 20, 2025
a9f866f
fix: Remove MAC address from sensitive data logging in mqtt_device_co…
eman Dec 20, 2025
72db555
fix: correct mixing valve alias and remove unused TOU status validator
eman Dec 20, 2025
13ba3f9
fix: Resolve type errors and linting issues for CI compatibility
eman Dec 20, 2025
b89b0e2
refactor: Reduce boilerplate in models with field factory
eman Dec 20, 2025
2a82414
Add device capabilities and advanced control features
eman Dec 22, 2025
285ea30
Round numeric values in CLI status output to one decimal place
eman Dec 22, 2025
f388c5a
Address review comments: Redact MAC addresses in logging
eman Dec 22, 2025
52e7db7
Fix security scanning: Use intermediate variable for redacted MAC add…
eman Dec 22, 2025
c067998
Address open PR review comments
eman Dec 22, 2025
f53e094
docs: Update documentation and changelog
eman Dec 23, 2025
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
8 changes: 8 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ Always run these checks before finalizing changes to ensure your code will pass

This prevents "passes locally but fails in CI" issues.

**IMPORTANT - Error Fixing Policy**:
- **Fix ALL linting and type errors**, even if they're in files you didn't modify or weren't introduced by your changes
- Pre-existing errors must be fixed as part of the task
- It's acceptable to fix unrelated errors in the codebase while completing a task
- Do not leave type errors or linting issues unfixed

**Important**: When updating CHANGELOG.rst or any file with dates, always use `date +"%Y-%m-%d"` to get the correct current date. Never hardcode or guess dates.

### Before Completing a Task - REQUIRED VALIDATION
Expand All @@ -65,6 +71,8 @@ This prevents "passes locally but fails in CI" issues.

**Do not mark a task as complete or create a PR without running all three checks.**

**CRITICAL - Fix ALL Errors**: Fix all linting and type errors reported by these tools, regardless of whether they exist in files you modified or were introduced by your changes. Pre-existing errors must be fixed as part of completing any task. This ensures a clean, passing test suite.

These checks prevent "works locally but fails in CI" issues and catch integration problems early.

Report the results of these checks in your final summary, including:
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*.orig
*.log
*.pot
*.csv
__pycache__/*
.cache/*
.*.swp
Expand Down Expand Up @@ -38,6 +39,10 @@ junit*.xml
coverage.xml
.pytest_cache/

# Diagnostics output
diagnostics_output/
examples/mqtt_diagnostics_output/

# Build and docs folder/files
build/*
dist/*
Expand Down
32 changes: 27 additions & 5 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
Changelog
=========

Version 7.1.0 (2025-12-19)
Version 7.1.0 (2025-12-22)
==========================

Added
-----

- **Device Capability System**: New device capability detection and validation framework:
- ``DeviceCapabilityChecker``: Validates device feature support based on device models
- ``DeviceInfoCache``: Efficient caching of device information with configurable update intervals
Expand All @@ -20,19 +21,40 @@ Added
- ``configure_reservation_water_program()``: Water program reservation management
- ``set_recirculation_mode()`` / ``configure_recirculation_schedule()`` / ``trigger_recirculation_hot_button()``: Recirculation pump control and scheduling

- **CLI Enhancements**: Extended command-line interface with new subcommands and diagnostics
- **New Examples**: Example scripts for demand response, air filter reset, vacation mode, recirculation control, and water program reservations
- **Documentation**: Enhanced device control documentation with capability matrix
- **CLI Documentation Updates**: Comprehensive documentation updates for subcommand-based CLI
- Complete rewrite of ``docs/python_api/cli.rst`` with full command reference
- Updated README.rst with new subcommand syntax and examples
- Added 8+ practical usage examples (cron jobs, automation, monitoring)
- Added troubleshooting guide and best practices section

- **Model Field Factory Pattern**: New field factory to reduce boilerplate in model definitions
- Automatic field conversion and validation
- Cleaner model architecture

Changed
-------

- **CLI Output**: Numeric values in status output now rounded to one decimal place for better readability
- ``MqttDeviceController`` now integrates device capability checking with auto-caching of device info
- Exception type hints improved with proper None handling in optional parameters
- CLI diagnostics output now available in structured format
- **MQTT Control Refactoring**: Centralized device control via ``.control`` namespace
- Standardized periodic request patterns
- Public API method ``ensure_device_info_cached()`` for better cache management
- **Logging Security**: Enhanced sensitive data redaction
- MAC addresses consistently redacted across all logging output
- Token logging removed from docstrings and examples
- Intermediate variables used for redacted data

Fixed
-----

- Type annotation consistency: Optional parameters now properly annotated as ``type | None`` instead of ``type``
- **Type System Fixes**: Resolved multiple type annotation issues for CI compatibility
- **Mixing Valve Field**: Corrected alias field name and removed unused TOU status validator
- **Vacation Days Validation**: Enforced maximum value validation for vacation mode days
- **CI Linting**: Fixed line length violations and import sorting issues
- **Security Scanning**: Resolved intermediate variable issues in redacted MAC address handling
- **Parser Regressions**: Fixed data parsing issues introduced in MQTT refactoring

Version 7.0.1 (2025-12-18)
==========================
Expand Down
137 changes: 84 additions & 53 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,66 +65,97 @@ Basic Usage
Command Line Interface
======================

The library includes a command line interface for quick monitoring and device information retrieval:
The library includes a command line interface for monitoring and controlling your Navien water heater:

.. code-block:: bash

# Set credentials via environment variables
export NAVIEN_EMAIL="your_email@example.com"
export NAVIEN_PASSWORD="your_password"

# Get current device status (one-time)
python -m nwp500.cli --status

# Get device information
python -m nwp500.cli --device-info

# Get device feature/capability information
python -m nwp500.cli --device-feature

# Turn device on
python -m nwp500.cli --power-on

# Turn device off
python -m nwp500.cli --power-off

# Turn device on and see updated status
python -m nwp500.cli --power-on --status

# Set operation mode and see response
python -m nwp500.cli --set-mode energy-saver

# Set DHW target temperature and see response
python -m nwp500.cli --set-dhw-temp 140

# Set temperature and then get updated status
python -m nwp500.cli --set-dhw-temp 140 --status

# Set mode and then get updated status
python -m nwp500.cli --set-mode energy-saver --status

# Just get current status (one-time)
python -m nwp500.cli --status

# Monitor continuously (default - writes to CSV)
python -m nwp500.cli --monitor

# Monitor with custom output file
python -m nwp500.cli --monitor --output my_data.csv

**Available CLI Options:**

* ``--status``: Print current device status as JSON. Can be combined with control commands to see updated status.
* ``--device-info``: Print comprehensive device information (firmware, model, capabilities) as JSON and exit
* ``--device-feature``: Print device capabilities and feature settings as JSON and exit
* ``--power-on``: Turn the device on and display response
* ``--power-off``: Turn the device off and display response
* ``--set-mode MODE``: Set operation mode and display response. Valid modes: heat-pump, energy-saver, high-demand, electric, vacation, standby
* ``--set-dhw-temp TEMP``: Set DHW (Domestic Hot Water) target temperature in Fahrenheit (115-150°F) and display response
* ``--monitor``: Continuously monitor status every 30 seconds and log to CSV (default)
* ``-o, --output``: Specify CSV output filename for monitoring mode
* ``--email``: Override email (alternative to environment variable)
* ``--password``: Override password (alternative to environment variable)
# Get current device status
python3 -m nwp500.cli status

# Get device information and firmware
python3 -m nwp500.cli info

# Get controller serial number
python3 -m nwp500.cli serial

# Turn device on/off
python3 -m nwp500.cli power on
python3 -m nwp500.cli power off

# Set operation mode
python3 -m nwp500.cli mode heat-pump
python3 -m nwp500.cli mode energy-saver
python3 -m nwp500.cli mode high-demand
python3 -m nwp500.cli mode electric
python3 -m nwp500.cli mode vacation
python3 -m nwp500.cli mode standby

# Set target temperature
python3 -m nwp500.cli temp 140

# Set vacation days
python3 -m nwp500.cli vacation 7

# Trigger instant hot water
python3 -m nwp500.cli hot-button

# Set recirculation pump mode (1-4)
python3 -m nwp500.cli recirc 2

# Reset air filter timer
python3 -m nwp500.cli reset-filter

# Enable water program mode
python3 -m nwp500.cli water-program

# View and update schedules
python3 -m nwp500.cli reservations get
python3 -m nwp500.cli reservations set '[{"hour": 6, "min": 0, ...}]'

# Time-of-use settings
python3 -m nwp500.cli tou get
python3 -m nwp500.cli tou set on

# Energy usage data
python3 -m nwp500.cli energy --year 2024 --months 10,11,12

# Demand response
python3 -m nwp500.cli dr enable
python3 -m nwp500.cli dr disable

# Real-time monitoring (logs to CSV)
python3 -m nwp500.cli monitor
python3 -m nwp500.cli monitor -o my_data.csv

**Global Options:**

* ``--email EMAIL``: Navien account email (or use ``NAVIEN_EMAIL`` env var)
* ``--password PASSWORD``: Navien account password (or use ``NAVIEN_PASSWORD`` env var)
* ``-v, --verbose``: Enable debug logging
* ``--version``: Show version and exit

**Available Commands:**

* ``status``: Show current device status (temperature, mode, power)
* ``info``: Show device information (firmware, capabilities)
* ``serial``: Get controller serial number
* ``power on|off``: Turn device on or off
* ``mode MODE``: Set operation mode (heat-pump, electric, energy-saver, high-demand, vacation, standby)
* ``temp TEMPERATURE``: Set target water temperature in °F
* ``vacation DAYS``: Enable vacation mode for N days
* ``recirc MODE``: Set recirculation pump (1=always, 2=button, 3=schedule, 4=temperature)
* ``hot-button``: Trigger instant hot water
* ``reset-filter``: Reset air filter maintenance timer
* ``water-program``: Enable water program reservation mode
* ``reservations get|set``: View or update schedule
* ``tou get|set STATE``: View or configure time-of-use settings
* ``energy``: Query historical energy usage (requires ``--year`` and ``--months``)
* ``dr enable|disable``: Enable or disable demand response
* ``monitor``: Monitor device status in real-time (logs to CSV with ``-o`` option)

Device Status Fields
====================
Expand Down
2 changes: 1 addition & 1 deletion docs/MQTT_DIAGNOSTICS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,7 @@ Device Control Integration
diagnostics.record_publish(queued=not mqtt_client.is_connected)

# Set temperature
await mqtt_client.set_dhw_temperature(device, 140.0)
await mqtt_client.control.set_dhw_temperature(device, 140.0)

if not mqtt_client.is_connected:
_logger.info(
Expand Down
4 changes: 2 additions & 2 deletions docs/guides/advanced_features_explained.rst
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ The ``outsideTemperature`` field is transmitted in the device status update. Pyt
.. code-block:: python

# From device status updates
status = await mqtt_client.get_status()
status = await mqtt_client.control.request_device_status()

# Access ambient temperature data
outdoor_temp = status.outside_temperature # Raw integer value
Expand Down Expand Up @@ -389,7 +389,7 @@ Monitoring Stratification from Python
async def monitor_stratification(mqtt_client: NavienMQTTClient, device_id: str):
"""Monitor tank stratification quality"""

status = await mqtt_client.get_status(device_id)
status = await mqtt_client.control.request_device_status(device_id)

upper_temp = status.tank_upper_temperature # float in °F
lower_temp = status.tank_lower_temperature # float in °F
Expand Down
6 changes: 3 additions & 3 deletions docs/guides/auto_recovery.rst
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ Create a new MQTT client instance when reconnection fails.

# Restore subscriptions
await mqtt_client.subscribe_device_status(device, on_status)
await mqtt_client.start_periodic_device_status_requests(device)
await mqtt_client.start_periodic_requests(device)

return mqtt_client

Expand Down Expand Up @@ -125,7 +125,7 @@ Refresh authentication tokens before retrying (handles token expiry).

# Restore subscriptions
await mqtt_client.subscribe_device_status(device, on_status)
await mqtt_client.start_periodic_device_status_requests(device)
await mqtt_client.start_periodic_requests(device)

**Pros:** Handles token expiry, more robust

Expand Down Expand Up @@ -183,7 +183,7 @@ Use exponential backoff between recovery attempts with token refresh.
await self.mqtt_client.subscribe_device_status(
self.device, self.callbacks['status']
)
await self.mqtt_client.start_periodic_device_status_requests(
await self.mqtt_client.start_periodic_requests(
self.device
)

Expand Down
14 changes: 7 additions & 7 deletions docs/guides/command_queue.rst
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ Basic Usage (Default Configuration)

# Command queue is enabled by default
# Commands sent during disconnection are automatically queued
await mqtt_client.request_device_status(device)
await mqtt_client.control.request_device_status(device)

# If disconnected, command is queued and sent on reconnection
# No user action needed
Expand Down Expand Up @@ -222,7 +222,7 @@ Handle Queue Full Condition
# Queue has max size of 100 by default
# Oldest commands automatically dropped when full
for i in range(150):
await mqtt_client.request_device_status(device)
await mqtt_client.control.request_device_status(device)
# First 100 queued, remaining 50 replace oldest

print(f"Queued: {mqtt_client.queued_commands_count}") # Will be 100
Expand Down Expand Up @@ -258,8 +258,8 @@ Reliable Device Control
.. code-block:: python

# Even during network issues, commands are preserved
await mqtt_client.set_dhw_temperature(device, 140.0)
await mqtt_client.set_dhw_mode(device, 2) # Energy Saver mode
await mqtt_client.control.set_dhw_temperature(device, 140.0)
await mqtt_client.control.set_dhw_mode(device, 2) # Energy Saver mode

# Commands queued if disconnected, sent when reconnected

Expand All @@ -269,7 +269,7 @@ Monitoring with Interruptions
.. code-block:: python

# Periodic status requests continue even with network issues
await mqtt_client.start_periodic_device_status_requests(device, 60)
await mqtt_client.start_periodic_requests(device, 60)

# Requests queued during disconnection, sent on reconnection

Expand All @@ -280,8 +280,8 @@ Batch Operations

# Send multiple commands without worrying about connection state
for device in devices:
await mqtt_client.request_device_status(device)
await mqtt_client.request_device_info(device)
await mqtt_client.control.request_device_status(device)
await mqtt_client.control.request_device_info(device)

# All commands reach their destination eventually

Expand Down
6 changes: 3 additions & 3 deletions docs/guides/energy_monitoring.rst
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,10 @@ Request detailed daily energy usage data for specific months:
await mqtt_client.subscribe_energy_usage(device, on_energy_usage)

# Request energy usage for September 2025
await mqtt_client.request_energy_usage(device, year=2025, months=[9])
await mqtt_client.control.request_energy_usage(device, year=2025, months=[9])

# Request multiple months
await mqtt_client.request_energy_usage(device, year=2025, months=[7, 8, 9])
await mqtt_client.control.request_energy_usage(device, year=2025, months=[7, 8, 9])

**Key Methods:**

Expand Down Expand Up @@ -243,7 +243,7 @@ Complete Energy Monitoring Example
)

# Request initial status
await mqtt_client.request_device_status(
await mqtt_client.control.request_device_status(
device.device_info.mac_address,
device.device_info.device_type,
device.device_info.additional_value
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/event_system.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Simple Event Handler

# Subscribe to status updates
await mqtt.subscribe_device_status(device, on_status_update)
await mqtt.request_device_status(device)
await mqtt.control.request_device_status(device)

# Monitor for 5 minutes
await asyncio.sleep(300)
Expand Down
Loading