nwp500.models package¶
Submodules¶
nwp500.models.device module¶
- class nwp500.models.device.DescalingInfo(*, descalingStartTime: str | None = None, descalingEndTime: str | None = None)[source]¶
Bases:
NavienBaseModelDescaling window reported by the REST API.
Both timestamps are
Noneon a device with no descaling scheduled or recorded.- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.device.Device(*, deviceInfo: DeviceInfo, location: Location, error: DeviceErrorSummary | None = None, descaling: DescalingInfo | None = None)[source]¶
Bases:
NavienBaseModelComplete device information including location.
- descaling: DescalingInfo | None¶
Present on both
/device/listand/device/info.
- device_info: DeviceInfo¶
- error: DeviceErrorSummary | None¶
Present on
/device/list; absent from/device/info.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- with_info(info: DeviceInfo) Self[source]¶
Return a new Device instance with updated DeviceInfo.
- class nwp500.models.device.DeviceErrorSummary(*, errorCode: Annotated[ErrorCode | int | None, _PydanticGeneralMetadata(union_mode='left_to_right')] = None, errorOccuredTime: str | None = None)[source]¶
Bases:
NavienBaseModelLast device fault as reported by the REST API.
The cloud keeps this independently of the live MQTT status, so it is readable without an MQTT connection - including while the device is offline, where it reports the fault as of the last time the device was heard from.
- error_code: ErrorCode | int | None¶
NO_ERRORwhen the device has no recorded fault, andNonewhen the cloud reports no code at all - it sends"errorCode": nullon some devices, which is not the same claim as “no error”. Typed to accept a bare int as well, followingdevice_type, so neither a null nor a code the enum does not know can make a whole/device/listresponse unparseable.union_modematters here: pydantic’s default smart union matches an incoming int against theintbranch exactly and never reaches the enum, so every code - known or not - would stay a plain int. Trying the branches left to right instead means a known code becomes anErrorCodemember and only an unknown one falls through toint.
- error_occurred_time: str | None¶
Spelled “Occured” by the API; the Python name is spelled correctly.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.device.DeviceInfo(*, homeSeq: int = 0, macAddress: str = '', additionalValue: str = '', deviceType: DeviceType | int = DeviceType.NPF700_WIFI, deviceName: str = 'Unknown', connected: ConnectionStatus, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.enum_validator.<locals>.validate, json_schema_input_type=PydanticUndefined)] = ConnectionStatus.DISCONNECTED, installType: str | None = None, modelTypeCode: int | None = None, installerId: str | None = None)[source]¶
Bases:
NavienBaseModelDevice information from API.
- connected: validate, json_schema_input_type=PydanticUndefined)]¶
- device_type: DeviceType | int¶
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.device.FirmwareInfo(*, macAddress: str = '', additionalValue: str = '', deviceType: DeviceType | int = DeviceType.NPF700_WIFI, curSwCode: int = 0, curVersion: int = 0, downloadedVersion: int | None = None, deviceGroup: str | None = None)[source]¶
Bases:
NavienBaseModelFirmware information for a device.
- device_type: DeviceType | int¶
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.device.Location(*, state: str | None = None, city: str | None = None, address: str | None = None, latitude: float | None = None, longitude: float | None = None, altitude: float | None = None)[source]¶
Bases:
NavienBaseModelLocation information for a device.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
nwp500.models.energy module¶
- class nwp500.models.energy.EnergyUsageBase(*, hpUsage: int = 0, heUsage: int = 0, hpTime: int = 0, heTime: int = 0)[source]¶
Bases:
NavienBaseModelBase energy usage fields common to daily and total responses.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.energy.EnergyUsageDay(*, hpUsage: int = 0, heUsage: int = 0, hpTime: int = 0, heTime: int = 0)[source]¶
Bases:
EnergyUsageBaseDaily energy usage data.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.energy.EnergyUsageResponse(*, total: EnergyUsageTotal, usage: list[MonthlyEnergyData])[source]¶
Bases:
NavienBaseModelResponse for energy usage query.
- get_month_data(year: int, month: int) MonthlyEnergyData | None[source]¶
Get energy usage data for a specific month.
- Parameters:
year – Year (e.g., 2025)
month – Month (1-12)
- Returns:
MonthlyEnergyData for that month, or None if not found
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- total: EnergyUsageTotal¶
- usage: list[MonthlyEnergyData]¶
- class nwp500.models.energy.EnergyUsageTotal(*, hpUsage: int = 0, heUsage: int = 0, hpTime: int = 0, heTime: int = 0)[source]¶
Bases:
EnergyUsageBaseTotal energy usage data.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.energy.MonthlyEnergyData(*, year: int, month: int, data: list[EnergyUsageDay])[source]¶
Bases:
NavienBaseModelMonthly energy usage data grouping.
- data: list[EnergyUsageDay]¶
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
nwp500.models.feature module¶
- class nwp500.models.feature.DeviceFeature(*, temperatureType: ~nwp500.enums.TemperatureType = TemperatureType.FAHRENHEIT, macAddress: str | None = None, countryCode: int, modelTypeCode: ~nwp500.enums.UnitType | int, controlTypeCode: int, volumeCode: ~typing.Annotated[~nwp500.enums.VolumeCode, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.enum_validator.<locals>.validate, json_schema_input_type=PydanticUndefined)], controllerSwVersion: int, panelSwVersion: int, wifiSwVersion: int, controllerSwCode: int, panelSwCode: int, wifiSwCode: int, recircSwVersion: int, recircModelTypeCode: int, controllerSerialNumber: str, powerUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, holidayUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, programReservationUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, dhwUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, dhwTemperatureSettingUse: ~nwp500.enums.DHWControlTypeFlag, smartDiagnosticUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, wifiRssiUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, tempFormulaType: ~nwp500.enums.TempFormulaType = TempFormulaType.ASYMMETRIC, energyUsageUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, freezeProtectionUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, mixingValueUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, drSettingUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, antiLegionellaSettingUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, hpwhUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, dhwRefillUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, ecoUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, electricUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, heatpumpUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, energySaverUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, highDemandUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, recirculationUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, recircReservationUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, title24Use: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, dhwTemperatureMin: int = None, dhwTemperatureMax: int = None, freezeProtectionTempMin: int = None, freezeProtectionTempMax: int = None, recircTemperatureMin: int = None, recircTemperatureMax: int = None)[source]¶
Bases:
NavienBaseModelDevice capabilities, configuration, and firmware info.
- Unit-aware note:
Temperature computed fields (e.g.
dhw_temperature_min,dhw_temperature_max,recirc_temperature_min) read the process-wide unit-system preference vianwp500.unit_system.get_unit_system()at access time, not at construction time. Changing the preference withnwp500.unit_system.set_unit_system()therefore affects values read from already-constructed instances, and the preference is shared across every async task and thread rather than being context-local (see issue #103).
- anti_legionella_setting_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- dhw_refill_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- dhw_temperature_setting_use: DHWControlTypeFlag¶
- dhw_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- dr_setting_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- eco_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- electric_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- energy_saver_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- energy_usage_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- freeze_protection_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- get_field_unit(field_name: str) str[source]¶
Get the correct unit suffix based on temperature preference.
Resolves dynamic units for temperature, flow rate, and volume fields that change based on unit system context override or the device’s temperature_type setting (Celsius or Fahrenheit).
- Parameters:
field_name – Name of the field to get the unit for
- Returns:
Unit string (e.g., “ °C”, “ LPM”, “ L”) or empty if field not found
- heatpump_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- high_demand_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- holiday_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- hpwh_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- mixing_valve_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- power_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- program_reservation_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- recirc_reservation_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- recirculation_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- smart_diagnostic_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- temp_formula_type: TempFormulaType¶
- temperature_type: TemperatureType¶
- title24_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- volume_code: validate, json_schema_input_type=PydanticUndefined)]¶
nwp500.models.mqtt_models module¶
- class nwp500.models.mqtt_models.MqttCommand(*, clientID: str, sessionID: str, requestTopic: str, responseTopic: str, request: MqttRequest | dict[str, Any], protocolVersion: int = 2)[source]¶
Bases:
NavienBaseModelRepresents an MQTT command message.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- request: MqttRequest | dict[str, Any]¶
- class nwp500.models.mqtt_models.MqttRequest(*, command: int, deviceType: DeviceType | int, macAddress: str, additionalValue: str = '...', mode: str | None = None, param: list[int | float] = <factory>, paramStr: str = '', month: list[int] | None = None, year: int | None = None)[source]¶
Bases:
NavienBaseModelMQTT command request payload.
- device_type: DeviceType | int¶
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
nwp500.models.schedule module¶
- class nwp500.models.schedule.OtaCommitPayload(*, swCode: int, swVersion: int)[source]¶
Bases:
NavienBaseModelPayload for committing a firmware component update.
Used with the OTA_COMMIT command (33554442). This command uses a special
commitOtastructure instead of the standard mode/param format.- Parameters:
sw_code – Software component code identifying which firmware to commit. 1 = Controller, 2 = Panel, 4 = WiFi/communication module.
sw_version – Version number to commit (as reported by the OTA check).
- model_config = {'alias_generator': None, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.schedule.RecirculationSchedule(*, schedule: list[RecirculationScheduleEntry] = <factory>)[source]¶
Bases:
NavienBaseModelComplete recirculation pump schedule (RECIR_RESERVATION command).
Used with command code 33554444 to configure timed recirculation pump operation windows.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- schedule: list[RecirculationScheduleEntry]¶
- class nwp500.models.schedule.RecirculationScheduleEntry(*, enable: int = 2, week: int = 0, startHour: int = 0, startMin: int = 0, endHour: int = 0, endMin: int = 0, mode: int = 1)[source]¶
Bases:
NavienBaseModelA single entry in a recirculation pump schedule.
Used with the RECIR_RESERVATION command (33554444) to set timed recirculation cycles. Each entry defines a time window and pump mode.
- Fields:
enable: 2=enabled, 1=disabled (device boolean)
week: bitfield of active days (Sun=bit7, Mon=bit6, …, Sat=bit1)
start_hour: 0-23
start_min: 0-59
end_hour: 0-23
end_min: 0-59
mode: recirculation mode (1=Constant, 2=Timer, 3=Temperature, 4=Sensor)
- model_config = {'alias_generator': None, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.schedule.ReservationEntry(*, enable: int = 2, week: int = 0, hour: int = 0, min: int = 0, mode: int = 1, param: int = 0)[source]¶
Bases:
NavienBaseModelA single scheduled reservation entry.
Wraps the raw 6-byte protocol fields and provides computed properties for display-ready values including unit-aware temperature conversion.
- The raw protocol fields are:
enable: 2=enabled, 1=disabled (device boolean)
week: bitfield of active days (Sun=bit7, Mon=bit6, …, Sat=bit1)
hour: 0-23
min: 0-59
mode: DHW operation mode ID (1-6)
param: temperature in half-degrees Celsius
- Unit-aware note:
The
temperatureandunitcomputed fields read the process-wide unit-system preference vianwp500.unit_system.get_unit_system()at access time, not at construction time. Changing the preference withnwp500.unit_system.set_unit_system()therefore affects values read from already-constructed instances, and the preference is shared across every async task and thread rather than being context-local (see issue #103).
- canonical_key() tuple[int, int, int, int, int, int][source]¶
Raw protocol fields as a stable, hashable tuple.
Used to compare a desired reservation entry against a device read-back without depending on field order or computed properties.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.schedule.ReservationSchedule(*, reservationUse: int = 0, reservation: list[ReservationEntry] = <factory>)[source]¶
Bases:
NavienBaseModelComplete reservation schedule from the device.
Can be constructed from raw MQTT response data. The
reservationfield accepts either a hex string (from GET responses) or a list of dicts/ReservationEntry objects.- canonical() tuple[bool, tuple[tuple[int, ...], ...]][source]¶
Normalized, order-independent representation of this schedule.
Entry order in a device read-back is not guaranteed to match the order a program was written in, so entries are sorted by their raw field tuple. Two schedules holding the same reservations return equal (and equally hashable) results from this method regardless of entry order — the intended way to compare a desired program against a device read-back (see
nwp500.reservations.update_reservations_confirmed()).
- property enabled: bool¶
Whether the reservation system is globally enabled.
Device bool convention: 2=on, 1=off.
- model_config = {'alias_generator': None, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- reservation: list[ReservationEntry]¶
- class nwp500.models.schedule.WeeklyReservationEntry(*, enable: int = 2, week: int = 0, hour: int = 0, min: int = 0, mode: int = 1, param: int = 0)[source]¶
Bases:
NavienBaseModelA single entry in a weekly temperature reservation schedule.
Similar to
ReservationEntrybut used with the RESERVATION_WEEKLY command (33554438), which configures a separate weekly temperature schedule independent of the timed reservation system.- The raw protocol fields mirror the standard reservation format:
enable: 2=enabled, 1=disabled (device boolean)
week: bitfield of active days (Sun=bit7, Mon=bit6, …, Sat=bit1)
hour: 0-23
min: 0-59
mode: DHW operation mode ID (1-6)
param: temperature in half-degrees Celsius
- Unit-aware note:
The
temperatureandunitcomputed fields read the process-wide unit-system preference vianwp500.unit_system.get_unit_system()at access time, not at construction time. Changing the preference withnwp500.unit_system.set_unit_system()therefore affects values read from already-constructed instances, and the preference is shared across every async task and thread rather than being context-local (see issue #103).
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.schedule.WeeklyReservationSchedule(*, reservationUse: int = 0, reservation: list[WeeklyReservationEntry] = <factory>)[source]¶
Bases:
NavienBaseModelComplete weekly reservation schedule (RESERVATION_WEEKLY command).
Used with command code 33554438 to configure a temperature schedule that repeats weekly. Accepts the same hex-encoded format as the standard reservation schedule.
- property enabled: bool¶
Whether the weekly reservation system is globally enabled.
Device bool convention: 2=on, 1=off.
- model_config = {'alias_generator': None, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- reservation: list[WeeklyReservationEntry]¶
nwp500.models.status module¶
- class nwp500.models.status.DeviceStatus(*, temperatureType: TemperatureType = TemperatureType.FAHRENHEIT, macAddress: str | None = None, command: int, specialFunctionStatus: int, errorCode: ErrorCode = ErrorCode.NO_ERROR, subErrorCode: int, smartDiagnostic: int, faultStatus1: int, faultStatus2: int, wifiRssi: int = None, dhwChargePer: float, drEventStatus: DREvent = DREvent.UNKNOWN, vacationDaySetting: int, vacationDayElapsed: int, antiLegionellaPeriod: int, programReservationType: int, tempFormulaType: TempFormulaType, outsideTemperature: int = None, currentStatenum: int, targetFanRpm: int, currentFanRpm: int, fanPwm: int, mixingRate: float, eevStep: int, airFilterAlarmPeriod: int, airFilterAlarmElapsed: int, cumulatedOpTimeEvaFan: int, cumulatedDhwFlowRate: int, touStatus: Annotated[bool, BeforeValidator(func=bool, json_schema_input_type=PydanticUndefined)], drOverrideStatus: int, touOverrideStatus: Annotated[bool, BeforeValidator(func=tou_override_to_python, json_schema_input_type=PydanticUndefined)], totalEnergyCapacity: Annotated[float, BeforeValidator(func=energy_count_to_wh, json_schema_input_type=PydanticUndefined)], availableEnergyCapacity: Annotated[float, BeforeValidator(func=energy_count_to_wh, json_schema_input_type=PydanticUndefined)], recircOperationMode: RecirculationMode, recircPumpOperationStatus: int, recircHotBtnReady: int, recircOperationReason: int, recircErrorStatus: int, currentInstPower: float, didReload: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], operationBusy: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)], freezeProtectionUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], dhwUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], dhwUseSustained: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], dhwOperationBusy: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, programReservationUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], ecoUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], compUse: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)], eevUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], evaFanUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], shutOffValveUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], conOvrSensorUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], wtrOvrSensorUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], antiLegionellaUse: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)], antiLegionellaOperationBusy: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)], errorBuzzerUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], currentHeatUse: HeatSource, heatUpperUse: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)], heatLowerUse: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)], scaldUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], airFilterAlarmUse: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)], recircOperationBusy: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], recircReservationUse: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)], dhwTemperature: int = None, dhwTemperatureSetting: int = None, dhwTargetTemperatureSetting: int = None, freezeProtectionTemperature: int = None, dhwTemperature2: int = None, hpUpperOnTempSetting: int = None, hpUpperOffTempSetting: int = None, hpLowerOnTempSetting: int = None, hpLowerOffTempSetting: int = None, heUpperOnTempSetting: int = None, heUpperOffTempSetting: int = None, heLowerOnTempSetting: int = None, heLowerOffTempSetting: int = None, heatMinOpTemperature: int = None, recircTempSetting: int = None, recircTemperature: int = None, recircFaucetTemperature: int = None, currentInletTemperature: int = None, currentDhwFlowRate: int, hpUpperOnDiffTempSetting: int, hpUpperOffDiffTempSetting: int, hpLowerOnDiffTempSetting: int, hpLowerOffDiffTempSetting: int, heUpperOnDiffTempSetting: int, heUpperOffDiffTempSetting: int, heLowerOnTDiffempSetting: int, heLowerOffDiffTempSetting: int, recircDhwFlowRate: int, tankUpperTemperature: int = None, tankLowerTemperature: int = None, dischargeTemperature: int = None, suctionTemperature: int = None, evaporatorTemperature: int = None, ambientTemperature: int = None, targetSuperHeat: int = None, currentSuperHeat: int = None, operationMode: CurrentOperationMode = CurrentOperationMode.STANDBY, dhwOperationSetting: DhwOperationSetting = DhwOperationSetting.ENERGY_SAVER, freezeProtectionTempMin: int = 12, freezeProtectionTempMax: int = 20)[source]¶
Bases:
NavienBaseModelRepresents the status of the Navien water heater device.
- Unit-aware note:
Temperature computed fields (e.g.
dhw_temperature,outside_temperature,dhw_temperature_setting) read the process-wide unit-system preference vianwp500.unit_system.get_unit_system()at access time, not at construction time. Changing the preference withnwp500.unit_system.set_unit_system()therefore affects values read from already-constructed instances, and the preference is shared across every async task and thread rather than being context-local (see issue #103).
- air_filter_alarm_use: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)]¶
- anti_legionella_operation_busy: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)]¶
- anti_legionella_use: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)]¶
- comp_use: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)]¶
- con_ovr_sensor_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- current_heat_use: HeatSource¶
- dhw_operation_busy: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- dhw_operation_setting: DhwOperationSetting¶
- dhw_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- dhw_use_sustained: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- did_reload: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- eco_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- eev_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- energy_to_setpoint: Annotated[float, BeforeValidator(func=energy_count_to_wh, json_schema_input_type=PydanticUndefined)]¶
- error_buzzer_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- eva_fan_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- freeze_protection_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- full_recovery_energy: Annotated[float, BeforeValidator(func=energy_count_to_wh, json_schema_input_type=PydanticUndefined)]¶
- get_field_unit(field_name: str) str[source]¶
Get the correct unit suffix based on temperature preference.
Resolves dynamic units for temperature, flow rate, and volume fields that change based on unit system context override or the device’s temperature_type setting (Celsius or Fahrenheit).
- Parameters:
field_name – Name of the field to get the unit for
- Returns:
Unit string (e.g., “ °C”, “ LPM”, “ L”) or empty if field not found
- heat_lower_use: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)]¶
- heat_upper_use: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)]¶
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- operation_busy: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)]¶
- operation_mode: CurrentOperationMode¶
- program_reservation_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- recirc_operation_busy: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- recirc_operation_mode: RecirculationMode¶
- recirc_reservation_use: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)]¶
- scald_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- shut_off_valve_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- temp_formula_type: TempFormulaType¶
- temperature_type: TemperatureType¶
- tou_override_status: Annotated[bool, BeforeValidator(func=tou_override_to_python, json_schema_input_type=PydanticUndefined)]¶
- property usable_energy: float¶
Energy drawable from the tank as useful hot water, in Wh.
Both device energy fields are measured from the setpoint, so neither is a state of charge - move the setpoint and both change while the water in the tank does not. Subtracting them cancels the setpoint and leaves the tank’s actual heat content:
full_recovery_energy - energy_to_setpoint = k * (setpoint - reference) - k * (setpoint - tank_temp) = k * (tank_temp - reference)
The reference is the device’s minimum operating temperature,
dhw_temperature_min(40.5 degC / 104.9 degF). That is close to the lowest temperature usable for a shower, so this is a good estimate of what can actually be drawn - water colder than that still holds heat, but not heat you can wash with.Robust in practice:
full_recovery_energyis bimodal, taking one of two values 2 degC apart at a fixed setpoint, but both fields shift together so the difference is unaffected. Checked against the tank thermistors over 12275 samples, the implied tank temperature agrees to a standard deviation of 0.57 degF.- Returns:
Drawable energy in Watt-hours, clamped at zero.
- nwp500.models.status.DeviceTriState¶
An on/off flag the device may decline to report. 0 becomes
Nonerather thanFalse, because the device reserves 0 to mean “unknown” and a fabricated OFF is worse than an absent value. Applied only to the fields the vendor’s own client decodes through its on/off enum - seedocs/explanation/unknown-values.rst.alias of
Annotated[bool|None,BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)]
nwp500.models.tou module¶
- class nwp500.models.tou.ConvertedTOUPlan(*, utility: str = '', name: str = '', schedule: list[TOUSchedule] = <factory>)[source]¶
Bases:
NavienBaseModelA rate plan converted by the Navien backend from OpenEI format.
Returned by POST /device/tou/convert. Contains the utility name, plan name, and device-ready schedule with season/week bitfields and scaled pricing.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- schedule: list[TOUSchedule]¶
- class nwp500.models.tou.TOUInfo(*, registerPath: str = '', sourceType: str = '', controllerId: str = '', manufactureId: str = '', name: str = '', utility: str = '', zipCode: int = 0, schedule: list[TOUSchedule] = <factory>)[source]¶
Bases:
NavienBaseModelTime of Use information.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- schedule: list[TOUSchedule]¶
- class nwp500.models.tou.TOUPeriod(*, season: int = 0, week: int = 0, startHour: int = 0, startMinute: int = 0, endHour: int = 0, endMinute: int = 0, priceMin: int = 0, priceMax: int = 0, decimalPoint: int = 5)[source]¶
Bases:
NavienBaseModelA single TOU pricing period from an MQTT
tou/rdresponse.Each period defines a time window, active season/week bitfields, and the pricing range for that window.
- Fields use camelCase aliases to match the raw MQTT payload:
season: bitfield of active months (bit N-1 set for month N)
week: bitfield of active weekdays (Sun=bit7, …, Sat=bit1)
startHour / startMinute: start of the time window (0-23 / 0-59)
endHour / endMinute: end of the time window (0-23 / 0-59)
priceMin / priceMax: encoded integer prices (divide by 10^decimalPoint)
decimalPoint: number of decimal places for price values
- Unit-aware note:
Unlike temperature-bearing models such as
DeviceStatus, the computed price and time fields here are independent of the process-wide unit-system preference (seenwp500.unit_system.get_unit_system()and issue #103); pricing is always decoded the same way regardless of the configured unit system.
- canonical_key() tuple[int, int, int, int, int, int, int, int, int][source]¶
Raw protocol fields as a stable, hashable tuple.
Used to compare a desired TOU period against a device read-back without depending on field order or computed properties.
- model_config = {'alias_generator': None, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.tou.TOUReservationSchedule(*, reservationUse: int = 0, reservation: list[TOUPeriod] = <factory>)[source]¶
Bases:
NavienBaseModelTOU schedule as returned by the MQTT
tou/rdresponse topic.This model matches the raw MQTT payload the device sends to confirm a
configure_tou_schedule()write. The device has no MQTT read for its TOU schedule -ctrl/tou/rdwithCommandCode.TOU_RESERVATIONis the write - so this payload only ever arrives as a write confirmation. To read the current schedule, use the RESTget_tou_info().The payload structure is:
{ "reservationUse": 2, # 0=disabled, 2=enabled "reservation": [ # list of TOU period dicts { "season": 4095, "week": 254, "startHour": 0, "startMinute": 0, "endHour": 23, "endMinute": 59, "priceMin": 10, "priceMax": 25, "decimalPoint": 2 }, ... ] }
- canonical() tuple[bool, tuple[tuple[int, ...], ...]][source]¶
Normalized, order-independent representation of this schedule.
Mirrors
nwp500.models.schedule.ReservationSchedule.canonical(): periods are sorted by their raw field tuple so two schedules with the same periods compare equal regardless of the order the device returned them in.
- property enabled: bool¶
Whether TOU scheduling is globally enabled.
Protocol convention: 0=disabled, 2=enabled.
- model_config = {'alias_generator': None, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.tou.TOUSchedule(*, season: int = 0, interval: list[dict[str, ~typing.Any]]=<factory>)[source]¶
Bases:
NavienBaseModelTime of Use schedule information.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Module contents¶
Data models for Navien NWP500 water heater communication.
This module defines data classes for representing data structures used in the Navien NWP500 water heater communication protocol.
These models are based on the MQTT message formats and API responses.
- class nwp500.models.ConvertedTOUPlan(*, utility: str = '', name: str = '', schedule: list[TOUSchedule] = <factory>)[source]¶
Bases:
NavienBaseModelA rate plan converted by the Navien backend from OpenEI format.
Returned by POST /device/tou/convert. Contains the utility name, plan name, and device-ready schedule with season/week bitfields and scaled pricing.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- schedule: list[TOUSchedule]¶
- class nwp500.models.DescalingInfo(*, descalingStartTime: str | None = None, descalingEndTime: str | None = None)[source]¶
Bases:
NavienBaseModelDescaling window reported by the REST API.
Both timestamps are
Noneon a device with no descaling scheduled or recorded.- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.Device(*, deviceInfo: DeviceInfo, location: Location, error: DeviceErrorSummary | None = None, descaling: DescalingInfo | None = None)[source]¶
Bases:
NavienBaseModelComplete device information including location.
- descaling: DescalingInfo | None¶
Present on both
/device/listand/device/info.
- device_info: DeviceInfo¶
- error: DeviceErrorSummary | None¶
Present on
/device/list; absent from/device/info.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- with_info(info: DeviceInfo) Self[source]¶
Return a new Device instance with updated DeviceInfo.
- class nwp500.models.DeviceErrorSummary(*, errorCode: Annotated[ErrorCode | int | None, _PydanticGeneralMetadata(union_mode='left_to_right')] = None, errorOccuredTime: str | None = None)[source]¶
Bases:
NavienBaseModelLast device fault as reported by the REST API.
The cloud keeps this independently of the live MQTT status, so it is readable without an MQTT connection - including while the device is offline, where it reports the fault as of the last time the device was heard from.
- error_code: ErrorCode | int | None¶
NO_ERRORwhen the device has no recorded fault, andNonewhen the cloud reports no code at all - it sends"errorCode": nullon some devices, which is not the same claim as “no error”. Typed to accept a bare int as well, followingdevice_type, so neither a null nor a code the enum does not know can make a whole/device/listresponse unparseable.union_modematters here: pydantic’s default smart union matches an incoming int against theintbranch exactly and never reaches the enum, so every code - known or not - would stay a plain int. Trying the branches left to right instead means a known code becomes anErrorCodemember and only an unknown one falls through toint.
- error_occurred_time: str | None¶
Spelled “Occured” by the API; the Python name is spelled correctly.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.DeviceFeature(*, temperatureType: ~nwp500.enums.TemperatureType = TemperatureType.FAHRENHEIT, macAddress: str | None = None, countryCode: int, modelTypeCode: ~nwp500.enums.UnitType | int, controlTypeCode: int, volumeCode: ~typing.Annotated[~nwp500.enums.VolumeCode, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.enum_validator.<locals>.validate, json_schema_input_type=PydanticUndefined)], controllerSwVersion: int, panelSwVersion: int, wifiSwVersion: int, controllerSwCode: int, panelSwCode: int, wifiSwCode: int, recircSwVersion: int, recircModelTypeCode: int, controllerSerialNumber: str, powerUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, holidayUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, programReservationUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, dhwUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, dhwTemperatureSettingUse: ~nwp500.enums.DHWControlTypeFlag, smartDiagnosticUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, wifiRssiUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, tempFormulaType: ~nwp500.enums.TempFormulaType = TempFormulaType.ASYMMETRIC, energyUsageUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, freezeProtectionUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, mixingValueUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, drSettingUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, antiLegionellaSettingUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, hpwhUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, dhwRefillUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, ecoUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, electricUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, heatpumpUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, energySaverUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, highDemandUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, recirculationUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, recircReservationUse: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, title24Use: ~typing.Annotated[bool, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, dhwTemperatureMin: int = None, dhwTemperatureMax: int = None, freezeProtectionTempMin: int = None, freezeProtectionTempMax: int = None, recircTemperatureMin: int = None, recircTemperatureMax: int = None)[source]¶
Bases:
NavienBaseModelDevice capabilities, configuration, and firmware info.
- Unit-aware note:
Temperature computed fields (e.g.
dhw_temperature_min,dhw_temperature_max,recirc_temperature_min) read the process-wide unit-system preference vianwp500.unit_system.get_unit_system()at access time, not at construction time. Changing the preference withnwp500.unit_system.set_unit_system()therefore affects values read from already-constructed instances, and the preference is shared across every async task and thread rather than being context-local (see issue #103).
- anti_legionella_setting_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- dhw_refill_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- dhw_temperature_setting_use: DHWControlTypeFlag¶
- dhw_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- dr_setting_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- eco_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- electric_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- energy_saver_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- energy_usage_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- freeze_protection_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- get_field_unit(field_name: str) str[source]¶
Get the correct unit suffix based on temperature preference.
Resolves dynamic units for temperature, flow rate, and volume fields that change based on unit system context override or the device’s temperature_type setting (Celsius or Fahrenheit).
- Parameters:
field_name – Name of the field to get the unit for
- Returns:
Unit string (e.g., “ °C”, “ LPM”, “ L”) or empty if field not found
- heatpump_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- high_demand_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- holiday_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- hpwh_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- mixing_valve_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- power_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- program_reservation_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- recirc_reservation_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- recirculation_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- smart_diagnostic_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- temp_formula_type: TempFormulaType¶
- temperature_type: TemperatureType¶
- title24_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- volume_code: validate, json_schema_input_type=PydanticUndefined)]¶
- class nwp500.models.DeviceInfo(*, homeSeq: int = 0, macAddress: str = '', additionalValue: str = '', deviceType: DeviceType | int = DeviceType.NPF700_WIFI, deviceName: str = 'Unknown', connected: ConnectionStatus, ~pydantic.functional_validators.BeforeValidator(func=~nwp500.converters.enum_validator.<locals>.validate, json_schema_input_type=PydanticUndefined)] = ConnectionStatus.DISCONNECTED, installType: str | None = None, modelTypeCode: int | None = None, installerId: str | None = None)[source]¶
Bases:
NavienBaseModelDevice information from API.
- connected: validate, json_schema_input_type=PydanticUndefined)]¶
- device_type: DeviceType | int¶
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.DeviceStatus(*, temperatureType: TemperatureType = TemperatureType.FAHRENHEIT, macAddress: str | None = None, command: int, specialFunctionStatus: int, errorCode: ErrorCode = ErrorCode.NO_ERROR, subErrorCode: int, smartDiagnostic: int, faultStatus1: int, faultStatus2: int, wifiRssi: int = None, dhwChargePer: float, drEventStatus: DREvent = DREvent.UNKNOWN, vacationDaySetting: int, vacationDayElapsed: int, antiLegionellaPeriod: int, programReservationType: int, tempFormulaType: TempFormulaType, outsideTemperature: int = None, currentStatenum: int, targetFanRpm: int, currentFanRpm: int, fanPwm: int, mixingRate: float, eevStep: int, airFilterAlarmPeriod: int, airFilterAlarmElapsed: int, cumulatedOpTimeEvaFan: int, cumulatedDhwFlowRate: int, touStatus: Annotated[bool, BeforeValidator(func=bool, json_schema_input_type=PydanticUndefined)], drOverrideStatus: int, touOverrideStatus: Annotated[bool, BeforeValidator(func=tou_override_to_python, json_schema_input_type=PydanticUndefined)], totalEnergyCapacity: Annotated[float, BeforeValidator(func=energy_count_to_wh, json_schema_input_type=PydanticUndefined)], availableEnergyCapacity: Annotated[float, BeforeValidator(func=energy_count_to_wh, json_schema_input_type=PydanticUndefined)], recircOperationMode: RecirculationMode, recircPumpOperationStatus: int, recircHotBtnReady: int, recircOperationReason: int, recircErrorStatus: int, currentInstPower: float, didReload: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], operationBusy: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)], freezeProtectionUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], dhwUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], dhwUseSustained: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], dhwOperationBusy: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)] = False, programReservationUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], ecoUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], compUse: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)], eevUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], evaFanUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], shutOffValveUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], conOvrSensorUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], wtrOvrSensorUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], antiLegionellaUse: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)], antiLegionellaOperationBusy: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)], errorBuzzerUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], currentHeatUse: HeatSource, heatUpperUse: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)], heatLowerUse: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)], scaldUse: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], airFilterAlarmUse: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)], recircOperationBusy: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)], recircReservationUse: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)], dhwTemperature: int = None, dhwTemperatureSetting: int = None, dhwTargetTemperatureSetting: int = None, freezeProtectionTemperature: int = None, dhwTemperature2: int = None, hpUpperOnTempSetting: int = None, hpUpperOffTempSetting: int = None, hpLowerOnTempSetting: int = None, hpLowerOffTempSetting: int = None, heUpperOnTempSetting: int = None, heUpperOffTempSetting: int = None, heLowerOnTempSetting: int = None, heLowerOffTempSetting: int = None, heatMinOpTemperature: int = None, recircTempSetting: int = None, recircTemperature: int = None, recircFaucetTemperature: int = None, currentInletTemperature: int = None, currentDhwFlowRate: int, hpUpperOnDiffTempSetting: int, hpUpperOffDiffTempSetting: int, hpLowerOnDiffTempSetting: int, hpLowerOffDiffTempSetting: int, heUpperOnDiffTempSetting: int, heUpperOffDiffTempSetting: int, heLowerOnTDiffempSetting: int, heLowerOffDiffTempSetting: int, recircDhwFlowRate: int, tankUpperTemperature: int = None, tankLowerTemperature: int = None, dischargeTemperature: int = None, suctionTemperature: int = None, evaporatorTemperature: int = None, ambientTemperature: int = None, targetSuperHeat: int = None, currentSuperHeat: int = None, operationMode: CurrentOperationMode = CurrentOperationMode.STANDBY, dhwOperationSetting: DhwOperationSetting = DhwOperationSetting.ENERGY_SAVER, freezeProtectionTempMin: int = 12, freezeProtectionTempMax: int = 20)[source]¶
Bases:
NavienBaseModelRepresents the status of the Navien water heater device.
- Unit-aware note:
Temperature computed fields (e.g.
dhw_temperature,outside_temperature,dhw_temperature_setting) read the process-wide unit-system preference vianwp500.unit_system.get_unit_system()at access time, not at construction time. Changing the preference withnwp500.unit_system.set_unit_system()therefore affects values read from already-constructed instances, and the preference is shared across every async task and thread rather than being context-local (see issue #103).
- air_filter_alarm_use: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)]¶
- anti_legionella_operation_busy: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)]¶
- anti_legionella_use: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)]¶
- comp_use: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)]¶
- con_ovr_sensor_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- current_heat_use: HeatSource¶
- dhw_operation_busy: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- dhw_operation_setting: DhwOperationSetting¶
- dhw_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- dhw_use_sustained: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- did_reload: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- eco_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- eev_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- energy_to_setpoint: Annotated[float, BeforeValidator(func=energy_count_to_wh, json_schema_input_type=PydanticUndefined)]¶
- error_buzzer_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- eva_fan_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- freeze_protection_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- full_recovery_energy: Annotated[float, BeforeValidator(func=energy_count_to_wh, json_schema_input_type=PydanticUndefined)]¶
- get_field_unit(field_name: str) str[source]¶
Get the correct unit suffix based on temperature preference.
Resolves dynamic units for temperature, flow rate, and volume fields that change based on unit system context override or the device’s temperature_type setting (Celsius or Fahrenheit).
- Parameters:
field_name – Name of the field to get the unit for
- Returns:
Unit string (e.g., “ °C”, “ LPM”, “ L”) or empty if field not found
- heat_lower_use: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)]¶
- heat_upper_use: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)]¶
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- operation_busy: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)]¶
- operation_mode: CurrentOperationMode¶
- program_reservation_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- recirc_operation_busy: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- recirc_operation_mode: RecirculationMode¶
- recirc_reservation_use: Annotated[bool | None, BeforeValidator(func=device_tristate_to_python, json_schema_input_type=PydanticUndefined)]¶
- scald_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- shut_off_valve_use: Annotated[bool, BeforeValidator(func=device_bool_to_python, json_schema_input_type=PydanticUndefined)]¶
- temp_formula_type: TempFormulaType¶
- temperature_type: TemperatureType¶
- tou_override_status: Annotated[bool, BeforeValidator(func=tou_override_to_python, json_schema_input_type=PydanticUndefined)]¶
- property usable_energy: float¶
Energy drawable from the tank as useful hot water, in Wh.
Both device energy fields are measured from the setpoint, so neither is a state of charge - move the setpoint and both change while the water in the tank does not. Subtracting them cancels the setpoint and leaves the tank’s actual heat content:
full_recovery_energy - energy_to_setpoint = k * (setpoint - reference) - k * (setpoint - tank_temp) = k * (tank_temp - reference)
The reference is the device’s minimum operating temperature,
dhw_temperature_min(40.5 degC / 104.9 degF). That is close to the lowest temperature usable for a shower, so this is a good estimate of what can actually be drawn - water colder than that still holds heat, but not heat you can wash with.Robust in practice:
full_recovery_energyis bimodal, taking one of two values 2 degC apart at a fixed setpoint, but both fields shift together so the difference is unaffected. Checked against the tank thermistors over 12275 samples, the implied tank temperature agrees to a standard deviation of 0.57 degF.- Returns:
Drawable energy in Watt-hours, clamped at zero.
- class nwp500.models.EnergyUsageBase(*, hpUsage: int = 0, heUsage: int = 0, hpTime: int = 0, heTime: int = 0)[source]¶
Bases:
NavienBaseModelBase energy usage fields common to daily and total responses.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.EnergyUsageDay(*, hpUsage: int = 0, heUsage: int = 0, hpTime: int = 0, heTime: int = 0)[source]¶
Bases:
EnergyUsageBaseDaily energy usage data.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.EnergyUsageResponse(*, total: EnergyUsageTotal, usage: list[MonthlyEnergyData])[source]¶
Bases:
NavienBaseModelResponse for energy usage query.
- get_month_data(year: int, month: int) MonthlyEnergyData | None[source]¶
Get energy usage data for a specific month.
- Parameters:
year – Year (e.g., 2025)
month – Month (1-12)
- Returns:
MonthlyEnergyData for that month, or None if not found
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- total: EnergyUsageTotal¶
- usage: list[MonthlyEnergyData]¶
- class nwp500.models.EnergyUsageTotal(*, hpUsage: int = 0, heUsage: int = 0, hpTime: int = 0, heTime: int = 0)[source]¶
Bases:
EnergyUsageBaseTotal energy usage data.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.FirmwareInfo(*, macAddress: str = '', additionalValue: str = '', deviceType: DeviceType | int = DeviceType.NPF700_WIFI, curSwCode: int = 0, curVersion: int = 0, downloadedVersion: int | None = None, deviceGroup: str | None = None)[source]¶
Bases:
NavienBaseModelFirmware information for a device.
- device_type: DeviceType | int¶
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.Location(*, state: str | None = None, city: str | None = None, address: str | None = None, latitude: float | None = None, longitude: float | None = None, altitude: float | None = None)[source]¶
Bases:
NavienBaseModelLocation information for a device.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.MonthlyEnergyData(*, year: int, month: int, data: list[EnergyUsageDay])[source]¶
Bases:
NavienBaseModelMonthly energy usage data grouping.
- data: list[EnergyUsageDay]¶
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.MqttCommand(*, clientID: str, sessionID: str, requestTopic: str, responseTopic: str, request: MqttRequest | dict[str, Any], protocolVersion: int = 2)[source]¶
Bases:
NavienBaseModelRepresents an MQTT command message.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- request: MqttRequest | dict[str, Any]¶
- class nwp500.models.MqttRequest(*, command: int, deviceType: DeviceType | int, macAddress: str, additionalValue: str = '...', mode: str | None = None, param: list[int | float] = <factory>, paramStr: str = '', month: list[int] | None = None, year: int | None = None)[source]¶
Bases:
NavienBaseModelMQTT command request payload.
- device_type: DeviceType | int¶
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Bases:
BaseModelBase model for all Navien models.
Provides: - camelCase alias generation (
to_camel) for JSON compatibility -populate_by_name=Trueso Python snake_case names work too -extra="ignore"to tolerate unknown protocol fields -use_enum_values=Falseto keep enum objects during validation - Custommodel_dumpthat converts enums to their.namestringConfiguration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Dump model to dict with enums serialised as their name strings.
Dump only the declared protocol fields, by alias.
Excludes pydantic
computed_fieldproperties, which are display-oriented (formatted times, weekday names, unit-converted temperatures) and must never be sent to the device.
- class nwp500.models.OtaCommitPayload(*, swCode: int, swVersion: int)[source]¶
Bases:
NavienBaseModelPayload for committing a firmware component update.
Used with the OTA_COMMIT command (33554442). This command uses a special
commitOtastructure instead of the standard mode/param format.- Parameters:
sw_code – Software component code identifying which firmware to commit. 1 = Controller, 2 = Panel, 4 = WiFi/communication module.
sw_version – Version number to commit (as reported by the OTA check).
- model_config = {'alias_generator': None, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.RecirculationSchedule(*, schedule: list[RecirculationScheduleEntry] = <factory>)[source]¶
Bases:
NavienBaseModelComplete recirculation pump schedule (RECIR_RESERVATION command).
Used with command code 33554444 to configure timed recirculation pump operation windows.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- schedule: list[RecirculationScheduleEntry]¶
- class nwp500.models.RecirculationScheduleEntry(*, enable: int = 2, week: int = 0, startHour: int = 0, startMin: int = 0, endHour: int = 0, endMin: int = 0, mode: int = 1)[source]¶
Bases:
NavienBaseModelA single entry in a recirculation pump schedule.
Used with the RECIR_RESERVATION command (33554444) to set timed recirculation cycles. Each entry defines a time window and pump mode.
- Fields:
enable: 2=enabled, 1=disabled (device boolean)
week: bitfield of active days (Sun=bit7, Mon=bit6, …, Sat=bit1)
start_hour: 0-23
start_min: 0-59
end_hour: 0-23
end_min: 0-59
mode: recirculation mode (1=Constant, 2=Timer, 3=Temperature, 4=Sensor)
- model_config = {'alias_generator': None, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.ReservationEntry(*, enable: int = 2, week: int = 0, hour: int = 0, min: int = 0, mode: int = 1, param: int = 0)[source]¶
Bases:
NavienBaseModelA single scheduled reservation entry.
Wraps the raw 6-byte protocol fields and provides computed properties for display-ready values including unit-aware temperature conversion.
- The raw protocol fields are:
enable: 2=enabled, 1=disabled (device boolean)
week: bitfield of active days (Sun=bit7, Mon=bit6, …, Sat=bit1)
hour: 0-23
min: 0-59
mode: DHW operation mode ID (1-6)
param: temperature in half-degrees Celsius
- Unit-aware note:
The
temperatureandunitcomputed fields read the process-wide unit-system preference vianwp500.unit_system.get_unit_system()at access time, not at construction time. Changing the preference withnwp500.unit_system.set_unit_system()therefore affects values read from already-constructed instances, and the preference is shared across every async task and thread rather than being context-local (see issue #103).
- canonical_key() tuple[int, int, int, int, int, int][source]¶
Raw protocol fields as a stable, hashable tuple.
Used to compare a desired reservation entry against a device read-back without depending on field order or computed properties.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.ReservationSchedule(*, reservationUse: int = 0, reservation: list[ReservationEntry] = <factory>)[source]¶
Bases:
NavienBaseModelComplete reservation schedule from the device.
Can be constructed from raw MQTT response data. The
reservationfield accepts either a hex string (from GET responses) or a list of dicts/ReservationEntry objects.- canonical() tuple[bool, tuple[tuple[int, ...], ...]][source]¶
Normalized, order-independent representation of this schedule.
Entry order in a device read-back is not guaranteed to match the order a program was written in, so entries are sorted by their raw field tuple. Two schedules holding the same reservations return equal (and equally hashable) results from this method regardless of entry order — the intended way to compare a desired program against a device read-back (see
nwp500.reservations.update_reservations_confirmed()).
- property enabled: bool¶
Whether the reservation system is globally enabled.
Device bool convention: 2=on, 1=off.
- model_config = {'alias_generator': None, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- reservation: list[ReservationEntry]¶
- class nwp500.models.TOUInfo(*, registerPath: str = '', sourceType: str = '', controllerId: str = '', manufactureId: str = '', name: str = '', utility: str = '', zipCode: int = 0, schedule: list[TOUSchedule] = <factory>)[source]¶
Bases:
NavienBaseModelTime of Use information.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- schedule: list[TOUSchedule]¶
- class nwp500.models.TOUPeriod(*, season: int = 0, week: int = 0, startHour: int = 0, startMinute: int = 0, endHour: int = 0, endMinute: int = 0, priceMin: int = 0, priceMax: int = 0, decimalPoint: int = 5)[source]¶
Bases:
NavienBaseModelA single TOU pricing period from an MQTT
tou/rdresponse.Each period defines a time window, active season/week bitfields, and the pricing range for that window.
- Fields use camelCase aliases to match the raw MQTT payload:
season: bitfield of active months (bit N-1 set for month N)
week: bitfield of active weekdays (Sun=bit7, …, Sat=bit1)
startHour / startMinute: start of the time window (0-23 / 0-59)
endHour / endMinute: end of the time window (0-23 / 0-59)
priceMin / priceMax: encoded integer prices (divide by 10^decimalPoint)
decimalPoint: number of decimal places for price values
- Unit-aware note:
Unlike temperature-bearing models such as
DeviceStatus, the computed price and time fields here are independent of the process-wide unit-system preference (seenwp500.unit_system.get_unit_system()and issue #103); pricing is always decoded the same way regardless of the configured unit system.
- canonical_key() tuple[int, int, int, int, int, int, int, int, int][source]¶
Raw protocol fields as a stable, hashable tuple.
Used to compare a desired TOU period against a device read-back without depending on field order or computed properties.
- model_config = {'alias_generator': None, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.TOUReservationSchedule(*, reservationUse: int = 0, reservation: list[TOUPeriod] = <factory>)[source]¶
Bases:
NavienBaseModelTOU schedule as returned by the MQTT
tou/rdresponse topic.This model matches the raw MQTT payload the device sends to confirm a
configure_tou_schedule()write. The device has no MQTT read for its TOU schedule -ctrl/tou/rdwithCommandCode.TOU_RESERVATIONis the write - so this payload only ever arrives as a write confirmation. To read the current schedule, use the RESTget_tou_info().The payload structure is:
{ "reservationUse": 2, # 0=disabled, 2=enabled "reservation": [ # list of TOU period dicts { "season": 4095, "week": 254, "startHour": 0, "startMinute": 0, "endHour": 23, "endMinute": 59, "priceMin": 10, "priceMax": 25, "decimalPoint": 2 }, ... ] }
- canonical() tuple[bool, tuple[tuple[int, ...], ...]][source]¶
Normalized, order-independent representation of this schedule.
Mirrors
nwp500.models.schedule.ReservationSchedule.canonical(): periods are sorted by their raw field tuple so two schedules with the same periods compare equal regardless of the order the device returned them in.
- property enabled: bool¶
Whether TOU scheduling is globally enabled.
Protocol convention: 0=disabled, 2=enabled.
- model_config = {'alias_generator': None, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.TOUSchedule(*, season: int = 0, interval: list[dict[str, ~typing.Any]]=<factory>)[source]¶
Bases:
NavienBaseModelTime of Use schedule information.
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.WeeklyReservationEntry(*, enable: int = 2, week: int = 0, hour: int = 0, min: int = 0, mode: int = 1, param: int = 0)[source]¶
Bases:
NavienBaseModelA single entry in a weekly temperature reservation schedule.
Similar to
ReservationEntrybut used with the RESERVATION_WEEKLY command (33554438), which configures a separate weekly temperature schedule independent of the timed reservation system.- The raw protocol fields mirror the standard reservation format:
enable: 2=enabled, 1=disabled (device boolean)
week: bitfield of active days (Sun=bit7, Mon=bit6, …, Sat=bit1)
hour: 0-23
min: 0-59
mode: DHW operation mode ID (1-6)
param: temperature in half-degrees Celsius
- Unit-aware note:
The
temperatureandunitcomputed fields read the process-wide unit-system preference vianwp500.unit_system.get_unit_system()at access time, not at construction time. Changing the preference withnwp500.unit_system.set_unit_system()therefore affects values read from already-constructed instances, and the preference is shared across every async task and thread rather than being context-local (see issue #103).
- model_config = {'alias_generator': <function to_camel>, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class nwp500.models.WeeklyReservationSchedule(*, reservationUse: int = 0, reservation: list[WeeklyReservationEntry] = <factory>)[source]¶
Bases:
NavienBaseModelComplete weekly reservation schedule (RESERVATION_WEEKLY command).
Used with command code 33554438 to configure a temperature schedule that repeats weekly. Accepts the same hex-encoded format as the standard reservation schedule.
- property enabled: bool¶
Whether the weekly reservation system is globally enabled.
Device bool convention: 2=on, 1=off.
- model_config = {'alias_generator': None, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': False, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- reservation: list[WeeklyReservationEntry]¶
- nwp500.models.fahrenheit_to_half_celsius(fahrenheit: float) int[source]¶
Convert Fahrenheit to half-degrees Celsius (for device commands).
- Parameters:
fahrenheit – Temperature in Fahrenheit.
- Returns:
Raw device value in half-Celsius format.
Example
>>> fahrenheit_to_half_celsius(140.0) 120
- nwp500.models.preferred_to_half_celsius(temperature: float) int[source]¶
Convert temperature from preferred unit to half-degrees Celsius.
Converts temperature from the user’s preferred unit (Celsius or Fahrenheit, based on global unit system context) to the half-Celsius format used by the device for commands and reservations.
- Parameters:
temperature – Temperature in user’s preferred unit (Celsius or Fahrenheit).
- Returns:
Raw device value in half-Celsius format.
Example
>>> # With us_customary unit system >>> preferred_to_half_celsius(140.0) # 140°F 120 >>> # With metric unit system >>> preferred_to_half_celsius(60.0) # 60°C 120
- nwp500.models.reservation_param_to_preferred(param: int) float[source]¶
Convert reservation param to user’s preferred temperature unit.
Device returns reservation temperatures as half-degrees Celsius (param). This converts them to the user’s preferred unit (Celsius or Fahrenheit) based on the global unit system context.
- Parameters:
param – Raw device value in half-Celsius format.
- Returns:
Temperature in user’s preferred unit (Celsius or Fahrenheit).
Example
>>> # With metric (Celsius) unit system >>> reservation_param_to_preferred(120) 60.0 >>> # With us_customary (Fahrenheit) unit system >>> reservation_param_to_preferred(120) 140.0