@mbcomer Yes, I guess this should be considered the new home for the the Navien native connectivity. If you have experience writing ESPHome components that’s awesome if you are willing to tackle that. If you choose to go that route, I might suggest checking out the ESPHome-Econet on GitHub. It seems like they have worked out quite a bit of this for the econet devices. I thought about trying to fork that, and copy/paste my way along, but it was a bit over my head. As for this protocol - I think it’s pretty close, but there are quite a few bytes that aren’t being used. After looking at the econet information, it made me wonder if there was additional information in those bytes that we weren’t using/accessing. Maybe understanding what is in the Navilink device for information that we haven’t already captured may help sort out anything that we are missing. Is that something you can help with?
As for the bytes that we have decoded so far, I’m pretty confident in the “structure”, but maybe not my calculations on the BTU, Therms, and kCal entities . Each of them change in an order of magnitude and behavior that I’d expect, but not sure I have calculated them right.
I have been successful in turning the unit on/off using my templated switch, however I have yet to get the setpoint to change, so I have currently removed that from my most recent config.
See screenshots:
As for the connector. I took some measurements using an inch based ruler it’s in 16th’s of an inch (sorry, it’s all I have ) for the connector, which led me to ordering a “kit” of JST-XH connectors that should be here Saturday. Maybe this, or the measurements below could help keep @aruffell out of his warm attic! Anyway - I’m not positive, but I think this may be the connector that would work. It looks like the only difference is that it does not have the locking mechanism like on the original Navien cable. I’d have to think that wouldn’t be too much of an issue, as there’s really limited/no vibration in the unit when it’s running.
Measurements on connector:
Most Recent Config:
substitutions:
comment: "redacted"
friendly_name: Navien Water Heater
logger_level: WARN
# Include basic info in all ESP Home Configs
# Basic Info Includes
# logger:
# wifi:
# ap:
# web_server:
# captive_portal:
# button: to restart the device
<<: !include .base1.yaml
binary_sensor:
- platform: status
name: Status
internal: True
esphome:
name: navien
friendly_name: Navien Water Heater
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
level: ${logger_level}
baud_rate: 0 #disable logging over uart
# Enable Home Assistant API
api:
encryption:
key: "redacted"
ota:
password: "redacted"
globals:
- id: init_set_temp
type: int
restore_value: yes
initial_value: '140'
#################################
#### UART SETUP/DECODE BYTES ####
#################################
uart:
id: uart_bus
tx_pin: 1
rx_pin: 3
baud_rate: 19200
data_bits: 8
stop_bits: 1
parity: NONE
debug:
direction: BOTH
dummy_receiver: True
after:
delimiter: "\n"
sequence:
- lambda: |-
//
// SPECIAL THANKS TO @suva on the HomeAssistant forum for Decoding these.
// https://community.home-assistant.io/t/navien-hot-water-heater-navilink/330044/92
//
UARTDebug::log_string(direction, bytes);
if (bytes[0] == 247 && bytes[1] == 5 && bytes[2] == 80 && bytes[3] == 80 && bytes[4] == 144 && bytes[5] == 34) {
// Water information
id(uart_water_byte0).publish_state(bytes[0]); //Common To all Packets
id(uart_water_byte1).publish_state(bytes[1]); //Common To all Packets
id(uart_water_byte2).publish_state(bytes[2]); //Packet id Byte 0
id(uart_water_byte3).publish_state(bytes[3]); //Packet id Byte 1
id(uart_water_byte4).publish_state(bytes[4]); //Packet id Byte 2
id(uart_water_byte5).publish_state(bytes[5]); //Data Length
id(uart_water_byte6).publish_state(bytes[6]);
id(uart_water_byte7).publish_state(bytes[7]);
id(uart_water_byte8).publish_state(bytes[8]);
id(uart_water_byte9).publish_state(bytes[9]); // System Power: high nibble: Unknown (values 0 and 0x20 observed); low nibble = 0=off 0x5=on
id(uart_water_byte10).publish_state(bytes[10]);
id(set_temp_read).publish_state((bytes[11]/2)); //Set temp - measured in 0.5 degrees C.
id(outlet_temp).publish_state((bytes[12]/2)); //Outlet temp - measured in 0.5 degrees C.
id(inlet_temp).publish_state((bytes[13]/2)); //Inlet temp - measured in 0.5 degrees C.
id(uart_water_byte14).publish_state(bytes[14]);
id(uart_water_byte15).publish_state(bytes[15]);
id(uart_water_byte16).publish_state(bytes[16]);
id(uart_water_byte17).publish_state(bytes[17]);
id(water_flow_lpm).publish_state(bytes[18] / 10); //Flow rate - measured in 0.1 liters per minute (divide by 10 to get LPM)
id(uart_water_byte19).publish_state(bytes[19]);
id(uart_water_byte20).publish_state(bytes[20]);
id(uart_water_byte21).publish_state(bytes[21]);
id(uart_water_byte22).publish_state(bytes[22]);
id(uart_water_byte23).publish_state(bytes[23]);
id(uart_water_byte24).publish_state(bytes[24]); //System status. Probably a bitwise field. Partially decoded: display units: 0x08 position: 1=metric 0=imperial. 0x02 position: 1=weekly 0=hotbutton
id(uart_water_byte25).publish_state(bytes[25]);
id(uart_water_byte26).publish_state(bytes[26]);
id(uart_water_byte27).publish_state(bytes[27]);
id(uart_water_byte28).publish_state(bytes[28]);
id(uart_water_byte29).publish_state(bytes[29]);
id(uart_water_byte30).publish_state(bytes[30]);
id(uart_water_byte31).publish_state(bytes[31]);
id(uart_water_byte32).publish_state(bytes[32]);
id(uart_water_byte33).publish_state(bytes[33]);
id(uart_water_byte34).publish_state(bytes[34]);
id(uart_water_byte35).publish_state(bytes[35]);
id(uart_water_byte36).publish_state(bytes[36]);
id(uart_water_byte37).publish_state(bytes[37]);
id(uart_water_byte38).publish_state(bytes[38]);
id(uart_water_byte39).publish_state(bytes[39]);
id(uart_water_byte40).publish_state(bytes[40]);
}
else if (bytes[0] == 247 && bytes[1] == 5 && bytes[2] == 80 && bytes[3] == 15 && bytes[4] == 144 && bytes[5] == 42) {
// Gas information
id(uart_gas_byte0).publish_state(bytes[0]); //Common to All Packets
id(uart_gas_byte1).publish_state(bytes[1]); //Common to All Packets
id(uart_gas_byte2).publish_state(bytes[2]); //Packet id Byte 0
id(uart_gas_byte3).publish_state(bytes[3]); //Packet id Byte 1
id(uart_gas_byte4).publish_state(bytes[4]); //Packet id Byte 2
id(uart_gas_byte5).publish_state(bytes[5]); //Data Length
id(uart_gas_byte6).publish_state(bytes[6]);
id(uart_gas_byte7).publish_state(bytes[7]);
id(uart_gas_byte8).publish_state(bytes[8]);
id(uart_gas_byte9).publish_state(bytes[9]);
id(uart_gas_byte10).publish_state(bytes[10]);
id(uart_gas_byte11).publish_state(bytes[11]);
id(uart_gas_byte12).publish_state(bytes[12]);
id(uart_gas_byte13).publish_state(bytes[13]);
id(uart_gas_byte14).publish_state(bytes[14]);
id(uart_gas_byte15).publish_state(bytes[15]);
id(uart_gas_byte16).publish_state(bytes[16]);
id(uart_gas_byte17).publish_state(bytes[17]);
id(uart_gas_byte18).publish_state(bytes[18]);
id(uart_gas_byte19).publish_state(bytes[19]);
id(uart_gas_byte20).publish_state(bytes[20]);
id(uart_gas_byte21).publish_state(bytes[21]);
id(low_byte_kcal).publish_state(bytes[22]); //Low byte current gas usage in kcal
id(high_byte_kcal).publish_state(bytes[23]); //High byte current gas usage in kcal
id(total_gas_use_m3).publish_state(bytes[24] / 10); //Total gas usage in 0.1m^3 (divide by 10 to get m^3)
id(uart_gas_byte25).publish_state(bytes[25]);
id(uart_gas_byte26).publish_state(bytes[26]);
id(uart_gas_byte27).publish_state(bytes[27]);
id(uart_gas_byte28).publish_state(bytes[28]);
id(uart_gas_byte29).publish_state(bytes[29]);
id(uart_gas_byte30).publish_state(bytes[30]);
id(uart_gas_byte31).publish_state(bytes[31]);
id(uart_gas_byte32).publish_state(bytes[32]);
id(uart_gas_byte33).publish_state(bytes[33]);
id(uart_gas_byte34).publish_state(bytes[34]);
id(uart_gas_byte35).publish_state(bytes[35]);
id(uart_gas_byte36).publish_state(bytes[36]);
id(uart_gas_byte37).publish_state(bytes[37]);
id(uart_gas_byte38).publish_state(bytes[38]);
id(uart_gas_byte39).publish_state(bytes[39]);
id(uart_gas_byte40).publish_state(bytes[40]);
} else {
// Handle other cases
}
###############################
#### ENABLE/DISABLE HEATER ####
###############################
switch:
- platform: template
name: "HW Heater On/Off"
id: navien_switch
restore_mode: ALWAYS_ON
icon: mdi:water-boiler
lambda: |-
if ((id(uart_water_byte9).state) == 5) {
return true;
id(navien_switch).publish_state(true);
} else {
return false;
id(navien_switch).publish_state(false);
}
turn_on_action:
- uart.write: !lambda
return {0xf7, 0x05, 0x0f, 0x50, 0x10, 0x0c, 0x4f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xce};
id(navien_switch).publish_state(true);
turn_off_action:
- uart.write: !lambda
return {0xf7, 0x05, 0x0f, 0x50, 0x10, 0x0c, 0x4f, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa};
id(navien_switch).publish_state(false);
- platform: template
name: "Fake Thermostat Switch"
id: fake_switch
restore_mode: ALWAYS_ON
optimistic: True
internal: True
####################################################################
#### SETPOINT ENTRY TEMPLATE - STILL WORKING ON THIS 4/29/2024 ####
####################################################################
climate:
- platform: thermostat
name: "Climate Controller"
visual:
min_temperature: 120 °F
max_temperature: 140 °F
temperature_step: 1.0 °F
sensor: outlet_temp
min_heating_off_time: 300s
min_heating_run_time: 300s
min_idle_time: 30s
heat_action:
- switch.turn_on: fake_switch
idle_action:
- switch.turn_off: fake_switch
################################
#### WATER TEMPLATE SENSORS ####
################################
sensor:
- platform: template
name: "WByte_0"
id: uart_water_byte0
internal: True
- platform: template
name: "WByte_01"
id: uart_water_byte1
internal: True
- platform: template
name: "WByte_02"
id: uart_water_byte2
internal: True
- platform: template
name: "WByte_03"
id: uart_water_byte3
internal: True
- platform: template
name: "WByte_04"
id: uart_water_byte4
internal: True
- platform: template
name: "WByte_05"
id: uart_water_byte5
internal: True
- platform: template
name: "WByte_06"
id: uart_water_byte6
internal: True
- platform: template
name: "WByte_07"
id: uart_water_byte7
internal: True
- platform: template
name: "WByte_08"
id: uart_water_byte8
internal: True
- platform: template
name: "WByte_09"
id: uart_water_byte9
internal: true
- platform: template
name: "WByte_10"
id: uart_water_byte10
internal: True
- platform: template
name: "Temp Set - Read"
id: set_temp_read
icon: mdi:thermometer-water
unit_of_measurement: °C
accuracy_decimals: 1
device_class: "temperature"
state_class: "measurement"
- platform: template
name: "Temp Outlet"
id: outlet_temp
icon: mdi:thermometer-water
unit_of_measurement: °C
accuracy_decimals: 1
device_class: "temperature"
state_class: "measurement"
- platform: template
name: "Temp Inlet"
id: inlet_temp
icon: mdi:thermometer-water
unit_of_measurement: °C
accuracy_decimals: 1
device_class: "temperature"
state_class: "measurement"
- platform: template
name: "WByte_14"
id: uart_water_byte14
internal: True
- platform: template
name: "WByte_15"
id: uart_water_byte15
internal: True
- platform: template
name: "WByte_16"
id: uart_water_byte16
internal: True
- platform: template
name: "WByte_17"
id: uart_water_byte17
internal: True
- platform: template
name: "Water Flow LPM"
id: water_flow_lpm #multiply by 0.2642 to get GPM
icon: mdi:water-percent
unit_of_measurement: l/min
- platform: template
name: "Water Flow GPM"
id: water_flow_gpm #multiply by 0.2642 to get GPM
icon: mdi:water-percent
unit_of_measurement: GPM
lambda: |-
return (id(water_flow_lpm).state * 0.2642);
- platform: template
name: "WByte_19 Unknown Flow"
id: uart_water_byte19
# internal: True
- platform: template
name: "WByte_20"
id: uart_water_byte20
internal: True
- platform: template
name: "WByte_21"
id: uart_water_byte21
internal: True
- platform: template
name: "WByte_22"
id: uart_water_byte22
internal: True
- platform: template
name: "WByte_23"
id: uart_water_byte23
internal: True
- platform: template
name: "WByte_24 Sys Status"
id: uart_water_byte24
# internal: True
- platform: template
name: "WByte_25"
id: uart_water_byte25
internal: True
- platform: template
name: "WByte_26"
id: uart_water_byte26
internal: True
- platform: template
name: "WByte_27"
id: uart_water_byte27
internal: True
- platform: template
name: "WByte_28"
id: uart_water_byte28
internal: True
- platform: template
name: "WByte_29"
id: uart_water_byte29
internal: True
- platform: template
name: "WByte_30"
id: uart_water_byte30
internal: True
- platform: template
name: "WByte_31"
id: uart_water_byte31
internal: True
- platform: template
name: "WByte_32"
id: uart_water_byte32
internal: True
- platform: template
name: "WByte_33"
id: uart_water_byte33
internal: True
- platform: template
name: "WByte_34"
id: uart_water_byte34
internal: True
- platform: template
name: "WByte_35"
id: uart_water_byte35
internal: True
- platform: template
name: "WByte_36"
id: uart_water_byte36
internal: True
- platform: template
name: "WByte_37"
id: uart_water_byte37
internal: True
- platform: template
name: "WByte_38"
id: uart_water_byte38
internal: True
- platform: template
name: "WByte_39"
id: uart_water_byte39
internal: True
- platform: template
name: "WByte_40"
id: uart_water_byte40
internal: True
##############################
#### GAS TEMPLATE SENSORS ####
##############################
- platform: template
name: "GByte_0"
id: uart_gas_byte0
internal: True
- platform: template
name: "GByte_01"
id: uart_gas_byte1
internal: True
- platform: template
name: "GByte_02"
id: uart_gas_byte2
internal: True
- platform: template
name: "GByte_03"
id: uart_gas_byte3
internal: True
- platform: template
name: "GByte_04"
id: uart_gas_byte4
internal: True
- platform: template
name: "GByte_05"
id: uart_gas_byte5
internal: True
- platform: template
name: "GByte_06"
id: uart_gas_byte6
internal: True
- platform: template
name: "GByte_07"
id: uart_gas_byte7
internal: True
- platform: template
name: "GByte_08"
id: uart_gas_byte8
internal: True
- platform: template
name: "GByte_09"
id: uart_gas_byte9
internal: true
- platform: template
name: "GByte_10"
id: uart_gas_byte10
internal: True
- platform: template
name: "GByte_11"
id: uart_gas_byte11
internal: True
- platform: template
name: "GByte_12"
id: uart_gas_byte12
internal: True
- platform: template
name: "GByte_13"
id: uart_gas_byte13
internal: True
- platform: template
name: "GByte_14"
id: uart_gas_byte14
internal: True
- platform: template
name: "GByte_15"
id: uart_gas_byte15
internal: True
- platform: template
name: "GByte_16"
id: uart_gas_byte16
internal: True
- platform: template
name: "GByte_17"
id: uart_gas_byte17
internal: True
- platform: template
name: "GByte_18"
id: uart_gas_byte18
internal: True
- platform: template
name: "GByte_19"
id: uart_gas_byte19
internal: True
- platform: template
name: "GByte_20"
id: uart_gas_byte20
internal: True
- platform: template
name: "GByte_21"
id: uart_gas_byte21
internal: True
- platform: template
name: "GByte_22"
id: uart_gas_byte22
internal: True
- platform: template
name: "GByte_23"
id: uart_gas_byte23
internal: True
- platform: template
name: "GByte_24"
id: uart_gas_byte24
internal: True
- platform: template
name: "GByte_25"
id: uart_gas_byte25
internal: True
- platform: template
name: "GByte_26"
id: uart_gas_byte26
internal: True
- platform: template
name: "GByte_27"
id: uart_gas_byte27
internal: True
- platform: template
name: "GByte_28"
id: uart_gas_byte28
internal: True
- platform: template
name: "GByte_29"
id: uart_gas_byte29
internal: True
- platform: template
name: "GByte_30"
id: uart_gas_byte30
internal: True
- platform: template
name: "GByte_31"
id: uart_gas_byte31
internal: True
- platform: template
name: "GByte_32"
id: uart_gas_byte32
internal: True
- platform: template
name: "GByte_33"
id: uart_gas_byte33
internal: True
- platform: template
name: "GByte_34"
id: uart_gas_byte34
internal: True
- platform: template
name: "GByte_35"
id: uart_gas_byte35
internal: True
- platform: template
name: "GByte_36"
id: uart_gas_byte36
internal: True
- platform: template
name: "GByte_37"
id: uart_gas_byte37
internal: True
- platform: template
name: "GByte_38"
id: uart_gas_byte38
internal: True
- platform: template
name: "GByte_39"
id: uart_gas_byte39
internal: True
- platform: template
name: "GByte_40"
id: uart_gas_byte40
internal: True
- platform: template
name: "Total Gas Use Cubic Meters"
id: total_gas_use_m3 # multiply by 0.3508 to get therms
icon: mdi:gas-cylinder
unit_of_measurement: m³
accuracy_decimals: 1
- platform: template
name: "Total Gas Use Therms"
id: total_gas_use_therms # multiply by 0.3508 to get therms
icon: mdi:gas-cylinder
unit_of_measurement: therms
accuracy_decimals: 1
lambda: |-
return (id(total_gas_use_m3).state * 0.358);
- platform: template
name: "kCal Low Byte"
id: low_byte_kcal
icon: mdi:gas-burner
unit_of_measurement: kCal
- platform: template
name: "kCal High Byte"
id: high_byte_kcal
icon: mdi:gas-burner
unit_of_measurement: kCal
- platform: template
name: "Instant BTUs"
id: instant_btus
icon: mdi:gas-burner
unit_of_measurement: "kbtu/hr"
accuracy_decimals: 3
state_class: "measurement"
lambda: |-
return max((id(outlet_temp).state - id(inlet_temp).state) * id(water_flow_gpm).state * 8.334 * 60 / 0.92 / 1000, 0.0);
# calculated as shown here https://www.plctalk.net/threads/low-byte-high-byte.78315/
- platform: template
name: "Total kCal"
id: total_kcal
icon: mdi:gas-burner
unit_of_measurement: kCal
accuracy_decimals: 2
state_class: "measurement"
lambda: |-
return (id(high_byte_kcal).state*256) + (id(low_byte_kcal).state);
Let me know if I can supply any further information.
Cheers!