Scheduling & Automation¶
The NWP500 supports four independent scheduling systems that work together to manage water heating. This guide covers all of them: reservations, time of use (TOU), vacation mode, and anti-legionella.
Overview¶
System |
Trigger Type |
Scope |
Priority |
Override Behavior |
|---|---|---|---|---|
Reservations |
Time-based (daily/weekly) |
Mode/Temperature changes |
Medium |
TOU and Vacation suspend reservations |
TOU |
Time + Price periods |
Heating behavior optimization |
Low-Medium |
Vacation suspends TOU; Reservations override |
Vacation |
Duration-based |
Complete suspension with maintenance ops |
Highest (blocks heating) |
Overrides all; only anti-legionella and freeze protection run |
Anti-Legionella |
Periodic cycle |
Temperature boost |
Highest (mandatory maintenance) |
Runs even during vacation; interrupts other modes |
All scheduling data is stored on the device and executes locally, so schedules continue to work even if the internet connection is lost.
Reservations¶
Reservations (also called “scheduled programs”) automatically change your water heater’s operating mode and temperature at specific times.
Quick Example¶
import asyncio
from nwp500 import (
NavienAuthClient,
NavienAPIClient,
NavienMqttClient,
build_reservation_entry,
)
async def main():
async with NavienAuthClient(
"email@example.com", "password"
) as auth:
api = NavienAPIClient(auth)
device = await api.get_first_device()
entry = build_reservation_entry(
enabled=True,
days=["Monday", "Tuesday", "Wednesday",
"Thursday", "Friday"],
hour=6,
minute=30,
mode_id=4, # High Demand
temperature=60.0 # In user's preferred unit
)
mqtt = NavienMqttClient(auth)
await mqtt.connect()
await mqtt.update_reservations(
device, [entry], enabled=True
)
await mqtt.disconnect()
asyncio.run(main())
CLI Usage¶
View current schedule:
# Table format (default)
nwp-cli reservations get
# JSON format
nwp-cli reservations get --json
Add a reservation:
nwp-cli reservations add \
--days "MO,TU,WE,TH,FR" \
--hour 6 --minute 30 \
--mode 4 --temp 60
Delete a reservation by index (1-based):
nwp-cli reservations delete 2
Update a reservation (partial — only specified fields change):
# Change temperature only
nwp-cli reservations update 1 --temp 55
# Change days and time
nwp-cli reservations update 1 --days "SA,SU" --hour 8 --minute 0
# Disable without deleting
nwp-cli reservations update 1 --disable
Set an entire schedule from JSON:
nwp-cli reservations set \
'[{"hour": 6, "min": 0, "mode": 3, "temp": 60,
"days": ["MO","TU","WE","TH","FR"]}]'
Note
The --days option accepts 2-letter abbreviations
(MO, TU, WE, TH, FR, SA, SU),
full day names (Monday, Tuesday, …), or a mix of both.
Temperatures always use the device’s configured unit system. The CLI auto-detects whether the device is set to Celsius or Fahrenheit.
Pydantic Models¶
The library provides ReservationEntry and ReservationSchedule
models for type-safe reservation handling.
ReservationEntry — A single reservation slot:
from nwp500 import ReservationEntry
entry = ReservationEntry(
enable=2, week=62, hour=6, min=30, mode=4, param=120
)
entry.enabled # True
entry.days # ['Tue', 'Wed', 'Thu', 'Fri', 'Sat']
entry.time # '06:30'
entry.temperature # 60.0 (in preferred unit)
entry.unit # '°C' or '°F'
entry.mode_name # 'High Demand'
ReservationSchedule — The full schedule (auto-decodes hex):
from nwp500 import ReservationSchedule
# From a device MQTT response (hex-encoded)
schedule = ReservationSchedule(
reservationUse=2,
reservation="023e061e0478"
)
schedule.enabled # True
schedule.reservation # [ReservationEntry(...)]
# From a list of entries
schedule = ReservationSchedule(
reservationUse=2,
reservation=[
ReservationEntry(
enable=2, week=62, hour=6, min=30,
mode=4, param=120
)
]
)
# Serialize back to raw fields (for protocol use)
for entry in schedule.reservation:
raw = entry.model_dump(
include={"enable", "week", "hour", "min",
"mode", "param"}
)
Entry Format¶
Each reservation entry has these fields:
enable(integer)Uses the standard device boolean convention:
1— disabled (stored but won’t execute)2— enabled (reservation will execute)
week(integer)Bitfield for days of the week (Monday-first):
Mon
Tue
Wed
Thu
Fri
Sat
Sun
bit 6 (64)
bit 5 (32)
bit 4 (16)
bit 3 (8)
bit 2 (4)
bit 1 (2)
bit 7 (128)
Common values:
Weekdays (Mon–Fri):
124(0b01111100)Weekends (Sat+Sun):
130(0b10000010)Every day:
254(0b11111110)Tue–Sat:
62(0b00111110)
hour(integer)Hour in 24-hour format (0–23).
min(integer)Minute (0–59).
mode(integer)DHW operation mode:
1— Heat Pump Only2— Electric Heater Only3— Energy Saver (Eco)4— High Demand5— Vacation Mode6— Power Off
param(integer)Target temperature in half-degrees Celsius:
Formula:
celsius = param / 2.0Examples: 70 → 35 °C (95 °F), 120 → 60 °C (140 °F)
Valid range: 35 °C to 65.5 °C (95 °F to 150 °F)
When using
build_reservation_entry(), pass the temperature in the user’s preferred unit and the conversion is automatic.
Helper Functions¶
build_reservation_entry() — Create a properly formatted entry:
from nwp500 import build_reservation_entry
entry = build_reservation_entry(
enabled=True,
days=["Monday", "Tuesday", "Wednesday",
"Thursday", "Friday"],
hour=6,
minute=30,
mode_id=4,
temperature=60.0 # In user's preferred unit
)
# {'enable': 2, 'week': 124, 'hour': 6, 'min': 30,
# 'mode': 4, 'param': 120}
The days parameter accepts:
Full names:
"Monday","Tuesday", …2-letter abbreviations:
"MO","TU","WE","TH","FR","SA","SU"Integer indices:
0(Monday) through6(Sunday)Any mix of the above
encode_week_bitfield() / decode_week_bitfield():
from nwp500.encoding import (
encode_week_bitfield,
decode_week_bitfield,
)
encode_week_bitfield(["MO", "TU", "WE", "TH", "FR"])
# 124
encode_week_bitfield([5, 6]) # Saturday + Sunday
# 130
decode_week_bitfield(62)
# ['Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
Managing Reservations¶
Important: The device protocol requires sending the full list of reservations for every update. Individual add/delete/update operations work by fetching the current schedule, modifying it, and sending the full list back.
CLI Helpers¶
The CLI provides convenience commands:
List current reservations:
nwp-cli reservations get # Formatted table
nwp-cli reservations get --json # JSON output
Add a single reservation:
nwp-cli reservations add --days MO,TU,WE,TH,FR \
--hour 6 --minute 30 --mode 4 --temperature 60
Update an existing reservation:
nwp-cli reservations update --mode 3 --temperature 58 1
Delete a reservation:
nwp-cli reservations delete 1
Library Helpers¶
The library provides convenience functions that abstract the read-modify-write pattern for individual reservation entries.
fetch_reservations() — Retrieve the current schedule:
from nwp500 import fetch_reservations
schedule = await fetch_reservations(mqtt, device)
if schedule is not None:
print(f"Schedule enabled: {schedule.enabled}")
for entry in schedule.reservation:
print(f" {entry.time} {', '.join(entry.days)}"
f" — {entry.temperature}{entry.unit}"
f" — {entry.mode_name}")
add_reservation() — Append a new entry to the schedule:
from nwp500 import add_reservation
await add_reservation(
mqtt, device,
enabled=True,
days=["MO", "TU", "WE", "TH", "FR"],
hour=6,
minute=30,
mode=4, # High Demand
temperature=60.0, # In user's preferred unit
)
delete_reservation() — Remove an entry by 1-based index:
from nwp500 import delete_reservation
await delete_reservation(mqtt, device, index=2)
update_reservation() — Modify specific fields of an existing entry. Only the keyword arguments you supply are changed; all others are kept:
from nwp500 import update_reservation
# Change temperature only
await update_reservation(mqtt, device, 1, temperature=55.0)
# Change days and time
await update_reservation(mqtt, device, 1, days=["SA", "SU"], hour=8, minute=0)
# Disable without deleting
await update_reservation(mqtt, device, 1, enabled=False)
These helpers raise ValueError for out-of-range arguments,
RangeValidationError or
ValidationError for device-protocol
violations. fetch_reservations() returns None on timeout and
logs the failure, while the mutating helpers (add_reservation(),
update_reservation(), delete_reservation()) raise
TimeoutError if the device does not respond.
Mode Selection Strategy¶
Choose the right mode for each time period:
Heat Pump (1) — Lowest cost, slowest recovery. Best for off-peak or overnight.
Energy Saver (3) — Balanced hybrid mode. Good for all-day use.
High Demand (4) — Fast recovery, higher cost. Use for scheduled peak demand (e.g., morning showers).
Electric (2) — Emergency only. Very high cost, fastest recovery. 72-hour operation limit.
Common Patterns¶
Weekday vs. weekend:
reservations = [
build_reservation_entry(
enabled=True,
days=[0, 1, 2, 3, 4], # Mon-Fri
hour=5, minute=30,
mode_id=4, temperature=60.0
),
build_reservation_entry(
enabled=True,
days=[5, 6], # Sat, Sun
hour=8, minute=0,
mode_id=3, temperature=55.0
),
]
Energy optimization (4-period weekday):
reservations = [
# 6 AM: High Demand for morning showers
build_reservation_entry(
enabled=True, days=["MO","TU","WE","TH","FR"],
hour=6, minute=0, mode_id=4, temperature=60.0
),
# 9 AM: Switch to Energy Saver
build_reservation_entry(
enabled=True, days=["MO","TU","WE","TH","FR"],
hour=9, minute=0, mode_id=3, temperature=50.0
),
# 5 PM: Heat Pump before peak pricing
build_reservation_entry(
enabled=True, days=["MO","TU","WE","TH","FR"],
hour=17, minute=0, mode_id=1, temperature=55.0
),
# 10 PM: Back to Energy Saver overnight
build_reservation_entry(
enabled=True, days=["MO","TU","WE","TH","FR"],
hour=22, minute=0, mode_id=3, temperature=50.0
),
]
Vacation automation:
reservations = [
# Friday 8 PM: Enter vacation mode
build_reservation_entry(
enabled=True, days=["FR"],
hour=20, minute=0, mode_id=5, temperature=50.0
),
# Sunday 2 PM: Return to Energy Saver
build_reservation_entry(
enabled=True, days=["SU"],
hour=14, minute=0, mode_id=3, temperature=55.0
),
]
Important Notes¶
The device can store up to ~16 reservation entries.
Reservations execute at the exact minute specified; the device checks every minute.
If the device is powered off, reservations will not execute.
Reservations persist through power cycles and internet outages.
Reservations are suspended when vacation mode or TOU is active.
Weekly Reservations¶
update_weekly_reservation() configures a separate weekly reservation payload
using WeeklyReservationSchedule. This is useful when you
want to send the whole weekly program as one typed object.
from nwp500 import (
WeeklyReservationEntry,
WeeklyReservationSchedule,
build_reservation_entry,
)
morning = WeeklyReservationEntry.model_validate(
build_reservation_entry(
enabled=True,
days=["MO", "TU", "WE", "TH", "FR"],
hour=6,
minute=0,
mode_id=4,
temperature=60.0,
)
)
day = WeeklyReservationEntry.model_validate(
build_reservation_entry(
enabled=True,
days=["MO", "TU", "WE", "TH", "FR"],
hour=9,
minute=0,
mode_id=3,
temperature=50.0,
)
)
schedule = WeeklyReservationSchedule(
reservationUse=2,
reservation=[morning, day],
)
await mqtt.update_weekly_reservation(device, schedule)
You can also subscribe to weekly reservation responses with
nwp500.mqtt.client.NavienMqttClient.subscribe_weekly_reservation_response().
Recirculation Scheduling¶
Recirculation schedules are represented by
RecirculationSchedule and
RecirculationScheduleEntry.
from nwp500 import RecirculationSchedule, RecirculationScheduleEntry
schedule = RecirculationSchedule(
schedule=[
RecirculationScheduleEntry(
enable=2,
week=124, # Mon-Fri
start_hour=6,
start_min=0,
end_hour=8,
end_min=30,
mode=2,
),
RecirculationScheduleEntry(
enable=2,
week=130, # Sat-Sun
start_hour=7,
start_min=0,
end_hour=9,
end_min=0,
mode=2,
),
]
)
await mqtt.configure_recirculation_schedule(device, schedule)
def on_recirculation(schedule):
for entry in schedule.schedule:
print(entry.start_time, entry.end_time, entry.mode_name)
await mqtt.subscribe_recirculation_schedule_response(device, on_recirculation)
Use nwp500.mqtt.client.NavienMqttClient.set_recirculation_mode() to switch
between always-on, button, schedule, and temperature modes.
Intelligent Scheduling¶
Intelligent scheduling enables the device’s adaptive heating mode.
await mqtt.enable_intelligent_scheduling(device)
# Later, return to manual scheduling behavior
await mqtt.disable_intelligent_scheduling(device)
This mode is separate from standard reservations. Use it when you want the heater to adapt automatically instead of following only fixed time windows.
Time of Use (TOU)¶
TOU scheduling allows price-aware heating optimization based on your utility’s electricity rate structure. For the full TOU guide including OpenEI integration, see Time of Use (TOU) Pricing.
TOU Period Structure¶
Each TOU period defines a time window with price information:
{
"season": 448, # Bitfield for months
# (bit 0=Jan … bit 11=Dec)
# 448 = Jun+Jul+Aug
"week": 124, # Bitfield for weekdays
# (same as reservations)
# 124 = Mon-Fri
"startHour": 9,
"startMinute": 0,
"endHour": 17,
"endMinute": 0,
"priceMin": 10, # Minimum price (encoded)
"priceMax": 25, # Maximum price (encoded)
"decimalPoint": 2 # Decimal places
# (2 = price/100 for dollars)
}
Season Bitfield¶
Months are encoded as a 12-bit field:
Jan |
Feb |
Mar |
Apr |
May |
Jun |
Jul |
Aug |
Sep |
Oct |
Nov |
Dec |
|---|---|---|---|---|---|---|---|---|---|---|---|
1 |
2 |
4 |
8 |
16 |
32 |
64 |
128 |
256 |
512 |
1024 |
2048 |
Summer (Jun–Aug):
32 + 64 + 128 = 224Winter (Dec–Feb):
1 + 2 + 2048 = 2051Year-round:
4095
How TOU Works¶
Device receives time periods with price ranges.
Low-price periods: Device uses heat pump only.
High-price periods: Device reduces heating or switches to lower-efficiency mode.
Peak periods: Device may pre-charge the tank before peak to minimize peak-time heating.
The device supports up to 16 TOU periods. Typical setups:
Simple: 3–4 periods (off-peak, shoulder, on-peak)
Moderate: 6–8 periods (split by season and weekday/weekend)
Complex: 12–16 periods (full seasonal tariff)
Example: Summer 3-Period Schedule¶
# Off-peak: 9 PM – 9 AM weekdays
off_peak = {
"season": 224, "week": 31,
"startHour": 21, "startMinute": 0,
"endHour": 9, "endMinute": 0,
"priceMin": 8, "priceMax": 10, "decimalPoint": 2
}
# Shoulder: 9 AM – 2 PM weekdays
shoulder = {
"season": 224, "week": 31,
"startHour": 9, "startMinute": 0,
"endHour": 14, "endMinute": 0,
"priceMin": 12, "priceMax": 18, "decimalPoint": 2
}
# Peak: 2 PM – 9 PM weekdays
peak = {
"season": 224, "week": 31,
"startHour": 14, "startMinute": 0,
"endHour": 21, "endMinute": 0,
"priceMin": 20, "priceMax": 35, "decimalPoint": 2
}
Vacation Mode¶
Vacation mode suspends heating for up to 99 days while maintaining critical functions.
Behavior¶
When vacation mode is active:
Heating suspended — no heat pump or electric heating cycles.
Freeze protection — still active. If temperature drops below 43 °F (6 °C), electric heating activates briefly.
Anti-legionella — still runs on schedule.
Automatic resumption — heating resumes 9 hours before the vacation end date.
Other schedules suspended — reservations and TOU are paused.
Duration: 0–99 days (0 = disabled, resumes immediately).
When to Use¶
Recommended for:
Extended absences (week-long trips or longer)
Seasonal properties
Emergency shutdown
Long maintenance periods
Not recommended for:
Weekend trips — use reservations instead
Work-day absences — use Energy Saver + TOU
Daily night-time suspension — use reservations with Heat Pump mode
Anti-Legionella¶
Anti-legionella periodically heats water to 70 °C (158 °F) for disinfection. This is a mandatory safety feature that runs even during vacation mode.
CLI Usage¶
# Enable with a 14-day cycle
nwp-cli anti-legionella enable --period 14
# Disable
nwp-cli anti-legionella disable
# Check current status
nwp-cli anti-legionella status
Period Configuration¶
Period (days) |
Use Case |
|---|---|
1–3 |
High-contamination risk environments |
7 |
High-risk installations or hard-water areas |
14 |
Standard residential (default) |
30 |
Commercial buildings with annual testing |
90 |
Well-maintained commercial systems with water treatment |
Risk Factors¶
Anti-legionella is especially important when:
Hard water areas (mineral deposits harbor bacteria)
Systems left unused for days (stagnant water)
Warm climates (25–45 °C ideal for legionella growth)
Recirculation systems (warm water sitting in pipes)
See Also¶
Time of Use (TOU) Pricing — Full TOU guide with OpenEI integration
MQTT Client — MQTT client API reference
Device Maintenance — Maintenance and OTA operations
Data Conversions and Units Reference — Temperature and power field conversions
Automatic Reconnection After Connection Failure — Handling temporary connectivity issues