Navien, ESP32 Navilink interface

Awesome! Since I still have my NaviLink connected, I may start back at the ground floor and connect my LA to see if I can fill in any of the blanks in the wire protocol decoding which we still have. After that I’ll connect up an ESP32 (just ordered a 485 interface for one) and will start a custom component for this.

Thanks!

1 Like

Received atom lite and rs485 expansion board for it over the weekend as well as XHB pigtail. Connected everything and works just fine. XH connector works. B signifies “buckle” for locking, but I guess it is not required as connector sits tight by itself.

1 Like

@brystmar It looks like @kkopachev has confirmed that the XH, or XHB connector will work for this. I have also confirmed the XH connector fits snugly when plugged into my device, though I didn’t order the “B” type with the buckle. I know you were looking to order one just thought I’d tag you in case you didn’t see this post.

@aruffell I finally had a chance to look through the pdf you linked above, and I realized after looking through it a bit, that appears to be for the Navien Boiler product line - not the Navien Hot Water Heater product line. Unless of course I missed something in my review - which is always possible. That said - I’m not sure how much of that cross references to the domestic hot water side. I did a quick search to see if there was a document like this for the NPE series, but I didn’t find one. Is there somewhere special you found this or was it just through a web search - that landed you on Navien’s page?

@mbcomer This is awesome news that you are heading towards a custom component for this! I’m certain that your code will be far more efficient at decoding, and reviewing the received information than mine! Anyway, just in case you haven’t looked at it, the information in this other post, may help too? Please let me know if I can be of assistance in anyway on that. My programming skills are limited in creating these, but I’m willing to try!

I appreciate the work of the group here! Thanks for everyone’s input and information. It is what makes this community great!


here is a picture of my install. Using Atomic RS485 base and atom lite controller. XHB pigtails from aliexpress

1 Like

I spent a little while trying to figure out what the checksum algorithm is with no luck (it doesn’t seem to be an ordinary CRC code - I tried several of the messages both including the header and without it).

It does seem like it’s something more complicated than (e.g.) adding up the bytes or XORing them together, because single bit changes will change anywhere between 1 and 3 bits in the checksum, e.g. in this example from https://community.home-assistant.io/t/navien-hot-water-heater-navilink/330044/93:

  f7 05 0f 50 10 0c 4f 00 0b 00 00 00 00 00 00 00 00 00 0a : off
  f7 05 0f 50 10 0c 4f 00 0a 00 00 00 00 00 00 00 00 00 ce : on

  f7 05 0f 50 10 0c 4f 00 00 00 00 01 00 00 00 00 00 00 6a : hotbutton press
  f7 05 0f 50 10 0c 4f 00 00 00 00 00 00 00 00 00 00 00 2a : hotbutton release

I have an NPE-210S, and bought the 5 pin to RJ-45 Navien cable. Then - using an RJ45 breakout connector. I measured with my DVM each one to ground, and found that there were 2 lines that were around 2.5-3VDC, and one line that was 12VDC. So, I think that the 5 pin connector (which only has 4 wires attached) actually is 2 RS-485 wires, 1 - Power, and 1 Ground wire. Then, I took a bit of time over the last few days to analyze the signal using my logic analyzer. I think I finally got it to read the signals without any framing errors. Additionally - I think it’s right as I see quite a bit more data when the unit is running and producing hot water than I do when it is idle.

It looks like the comm specs are 19200, 8, NONE,1 It seems that RS485-A is MSB-first, and Inverted. and RS485-B is MSB-first, and NOT Inverted.

My end goal here would be to utilize ESPHOME to interface with this unit and be able to read the data that is being transmitted on these communication lines. Maybe even using the 12VDC from the unit to power the ESP?

Now for the part that might be a bit over my head – using the decoder to interpret the data. Thoughts on where/How to start?

2 Likes

RS-485 is really just standard serial / UART at the protocol level. It’s a little different at the physical level in that it is a differential set of wires to help reject noise and make it more reliable in harsh noisy environments. (So, instead of say 3 wires - gnd, tx, rx there will be five wires - gnd, rx-, rx+, tx-, tx+) (this is assuming separate rx/tx). RS-485 can also operate at higher differential voltages, again to support better high-noise communication (higher signal-to-noise-ratio).

In any case - I just got the Lite unit hooked up to my water heater today. I’ll get to some data sniffing in the next few days hopefully!

1 Like

@mbcomer - Awesome News that someone else is going down this road too! Glad to hear you have a Lite unit all hooked up and something to sniff the data lines from. I have been trying different things here - and struggling a bit to understand your statement above regarding the 5 wire’s vs what I thought I knew about RS-485 (I thought the RS422 was 5 wire?). As I noted above, the cable only has 4 wires of the 8 in the RJ-45 connector. This was verified and continuity tested. If my DVM was correct, it seems that I have 12VDC, GND, TX+, and TX- for the communications. I’m curious, does the Navilink Lite get power from the RJ-45 or does it need it’s own power connector? If so, that likely explains the 12VDC. Based on this video, it seems I have a different connector. My connector only has 4 wires. I went and put jumpers on all 5 pins in that connector (directly on the board) - the 5th place in that connector does not appear to have any signal/voltage on it. Anyway, here is a snip of what I see from the logic analyzer on just the raw data being transmitted from the unit. At the time of the screenshot I had disconnected the 12VDC - as it didn’t seem necessary to monitor any longer. Also, it appears that either have quite a bit of noise on one of the lines. I have read 120ohm resistor could help that? You can see it to the left of the RS-485B signal line near the beginning of the screenshot.

Then, I setup the decoding. Either I don’t understand what it is telling me or I have something setup wrong on the decoding front. Thoughts here are welcomed! I do see that I get some framing errors at first (within the first few bits) but they go away after that. I have read in a few other places - where RS-485 requires some “math” to get the right data as it is a differential protocol. It does seem that the wave forms from the first capture are just that - inverted from each other.

Appreciate any further direction from this information, and insight into where to turn next.

1 Like

If they were similar in price, is it better to get Navi Link or Navi Link Lite?

depends how many Navien water heaters you have. If you have one, then get the Navi-Link Lite.

Thanks, I ordered the Lite version. I’m so looking forward to make water heating smarter :smile: Getting stats is the first step.

Ultimately I want to make the hot button recirc intelligent , like described here:

I built an RS485 sniffer similar to this one: RS485 sniffer

I used the UART settings @tsquared provided (19200, 8N1) (thanks!), and connected the sniffer to a tap on the cat5 cable provided with a Navilink lite. Using this cable, the blue and blue-white wires appear to be the RS485 signals. I connected blue to RS485-B and blue-white to RS485-A.

With no navilink attached, the water heater continuously emits two alternating packets (with a short interval between each). I believe each of them contains continuous status information. Let’s call them A and B. With a navilink connected, the navilink sends an announcement packet (let’s call it C) after each packet from the heater. The result is a stream that looks like: A C B C A C B C…

There is a fourth type of packet when the navilink sends a command. It appears the navilink simply repeats the command several times (typically 6), with no acknowledgement from the heater.

I observed the packets while performing various operations, and I believe I have decoded most of the data and commands available in the navilink app. Here are the packet captures and what I have identified so far.

Common Fields

The first 6 bytes appear to be a packet header. The first two bytes are common to all packets. The next three uniquely identify each of our three packet types. Byte index 5 is the length of the data, not including the header (6 bytes) or the final byte, which I believe is a checksum. In other words, byte index 5 is the length of the packet minus 7 bytes.

Index Value Notes
0 f7 Common to all packets
1 05 Common to all packets
2 Packet id byte 0
3 Packet id byte 1
4 Packet id byte 2
5 Data length
Data
N Checksum

Packet A (from heater)

This packet seems to have information related to water.

Here is a full example packet:

f7 05 50 50 90 22 42 00 00 05 14 72 37 2e 00 00 00 00 00 00 f8 8e 00 00 02 00 00 00 05 00 07 00 00 02 00 00 00 00 00 00 67

And the fields decoded so far:

Index Value Notes
0 f7 Common to all packets
1 05 Common to all packets
2 50 Packet id byte 0
3 50 Packet id byte 1
4 90 Packet id byte 2
5 22 Data length
09 05 System power: high nibble: unknown (values 0 and 0x20 observed); low nibble: 0=off 0x5=on
11 72 Set temp - measured in 0.5 degrees C. 57C in this case
12 37 Outlet temp - measured in 0.5 degrees C. 27.5C in this case
13 2e Unconfirmed, but possibly inlet temperature? This seems very close to the inlet temperature, but was usually off by 0.5-1.5C. My inlet temperature did not vary enough to analyze more
18 00 Unknown, but appeared proportional to water flow rate
19 00 Flow rate - measured in 0.1 liters per minute (divide by 10 to get LPM)
24 02 System status. Probably a bitwise field. Partially decoded: display units: 0x08 position: 1=metric 0=imperial. 0x02 position: 1=weekly 0=hotbutton

Packet B (from heater)

This packet seems to have information related to gas.

Here is a full example packet:

f7 05 50 0f 90 2a 45 00 0b 01 0c 03 17 00 72 6d 23 00 00 00 00 00 00 00 61 00 00 00 0b 00 1d 00 f6 34 00 00 05 00 00 00 00 00 aa 48 00 00 01 00 b3

And the fields decoded so far:

Index Value Notes
0 f7 Common to all packets
1 05 Common to all packets
2 50 Packet id byte 0
3 0f Packet id byte 1
4 90 Packet id byte 2
5 2a Data length
22 00 Low byte current gas usage in kcal
23 00 High byte current gas usage in kcal
24 61 Total gas usage in 0.1m^3 (divide by 10 to get m^3; 9.7 cubic meters in this case)

Packet C (from navilink)

This appears to be an announcement packet. It tells the heater there is a navilink attached. The heater uses this to disable local setting of the weekly schedule and instead require setting the schedule from the app.

Here is a full example packet:

f7 05 0f 50 10 03 4a 00 01 55

Packet D (from navilink)

This is the command packet. I tested each of the following commands: power off, power on, set temp, hotbutton. Each type of command uses a different field. I only ever saw a single field used since I tested it using the navilink app, but the format suggests that more than one command could be combined into a single packet. The navilink repeats the on/off/set commands 6 times. For hotbutton presses, it sends the on command twice followed by the off command once.

Here is a full example:

f7 05 0f 50 10 0c 4f 00 0b 00 00 00 00 00 00 00 00 00 0a

Index Value Notes
0 f7 Common to all packets
1 05 Common to all packets
2 0f Packet id byte 0
3 50 Packet id byte 1
4 10 Packet id byte 2
5 0c Data length
8 0b Power: 0x0a=on 0x0b=off
9 00 Set temperature (units of 0.5C: 0x74 is 58C)
11 00 Hotbutton: 0x01=press 0x00=release

Here are all the commands I have seen as full packets (so you can see the checksums):

  f7 05 0f 50 10 0c 4f 00 0b 00 00 00 00 00 00 00 00 00 0a : off
  f7 05 0f 50 10 0c 4f 00 0a 00 00 00 00 00 00 00 00 00 ce : on
  f7 05 0f 50 10 0c 4f 00 00 74 00 00 00 00 00 00 00 00 ea : set to 58
  f7 05 0f 50 10 0c 4f 00 00 72 00 00 00 00 00 00 00 00 c4 : set to 57
  f7 05 0f 50 10 0c 4f 00 00 00 00 01 00 00 00 00 00 00 6a : hotbutton press
  f7 05 0f 50 10 0c 4f 00 00 00 00 00 00 00 00 00 00 00 2a : hotbutton release

Checksum

I believe the final byte is a checksum, but I have not identified the algorithm yet. It would be useful to do so for the commands (especially the temperature set commands).

4 Likes

the easiest option for me was having the bathroom light switch trigger the hot button.

So far this project has been very interesting. I am learning at every turn! This is great information! Thank You @suva! I took what you had, and tried with my limited python skills to pull it into python directly using a RS485/USB adapter. Also, built an ESPHome UART Sniffer as you describe above. In both cases - essentially, It reads the information you show above!! I am now trying to figure out how to manipulate the data to get the respective bits/bytes/strings out of this information.

This brings me to my next question - although you note, and I can see the f7 05 common data for all, the data streamed from the COM port starts long before I can see those values.

For example: The raw stream looks like this - (I **'d the f7 05 for clarity - it shows up 2x in this example.)

b'\xd8\xbc\x1a\x00\xb0\x02\x00\x00\x00\x00\xa6I\x00\x00\x00\x00\xb9\**xf7\x05PP**\x90"B\x00\x00\x05\x14x0*\x00\x00\x00\x00\x00\x00\x00\xaf\x00\x18\x00\x00\x00\x00\xb0\x02\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\xbb**\xf7\x05P**\x0f\x90*E\x00\x01\x01\x14\x07"\x00x0\x1b\x00\x00\x00\x00\x01\x00\x00\x9f\x1f\x00\x00\xd1\x03Z\n'

Interesting enough - that data stream also shows some weird values like the P or PP next to the 05 among other oddities. I have been able to convert it directly to a hex stream and received the following results which appear more accurate:

0xd8bc1a00b00200000000a64900000000b9f70550509022420000051478302a00000000000000af001800000000b00200000002000000000000bbf705500f902a450001011407220078301b000000000100009f1f0000d1035a0a

It seems like I should be able to slice the hex data stream better than the raw one as it appears more accurate? I am grateful for all the help!

Anyone on Next thoughts on python libraries, or ESPHome coding that helps break this down into a useable format?

I did find this post, but it doesn’t seem “solved”. It links to this post, which talks about custom devices in ESPHome, which currently appears past my abilities. I guess I’m back to the reading and learning phase!

Ok - I worked my way through some really crude python and I have it displaying some information regarding the water, and printing to the REPL. Now that I can actually read it for what data it is supposed to be showing, It appears that I may have some differences from what @suva shows.

As it relates to index 18 & 19 (flow) on the water side. It seems that my water flow is actually index 18, as index 19 never changes for me. Index 19 is always zero. I confirmed that the front panel shows the value I am calculating on index 18 in the code, back to GPM.

Also, index 13 - that value seems strange to be inlet temperature. Guess I will have to keep watching and comparing that value to the front panel of the unit and see what it says.

I got my module today. Navilink Lite is using STM32F412 for MCU and MXCHIP EMC3280-E for WIFI.

Making some progress here! Again - thanks to @suva for all his decoding! I started with some generic python code to make sure I could actually talk/receive information from the device. The code I wrote to receive the information from my equipment is below. I am certain that it’s not efficient, and likely doesn’t follow too many rules for coding. If anyone has any tips here they are appreciated!

import time
import serial

print ("Opening port")
try:
  ser = serial.Serial('COM3',19200,bytesize=8,
                    parity=serial.PARITY_NONE,
                    stopbits=serial.STOPBITS_ONE,
                    timeout=0)
  print ("Port is open")

except serial.SerialException:
  serial.Serial('COM3', 19200).close()
  print ("Port is closed")
  serial_port = serial.Serial("COM3",19200)
  print ("Port is open again")

print ("Ready to use")

buffer = bytearray(b'')
buffer_length = 99
buf_list = []


while True:

  try:
    buffer += ser.read()
    if len(buffer) <= buffer_length:
      buffer += buffer
    elif len(buffer) >= buffer_length:
      buf_list = (list(buffer))
      # print(buf_list)
      ser.flushInput()
      buffer = bytearray(b'')
      # print(buf_list[0:23])
    if buf_list[5] == 34:
      # print("Water Info Received!")
      sys_power = buf_list[9]
      sys_power_text = "Off"
      set_temp = (((buf_list[11]/2)*9/5) + 32)
      outlet_temp = (((buf_list[12]/2)*9/5) + 32)
      inlet_temp = (((buf_list[13]/2)*9/5) + 32)
      wtr_flow = round(((buf_list[18]/10)* 0.2642),2)
      unknown_flow = buf_list[19]
      print(" ")
      print("*************Water Information**************")
      print(" ")
      if sys_power == 5:
        sys_power_text= "    On"
        print("The Power is:   ",sys_power_text)
      elif sys_power ==0:
        sys_power_text= "    Off"
        print("The Power is:   ",sys_power_text)
      else:
        print("The Power is Unknown")
      print("The Outlet Temp is: ",outlet_temp,"",u"\u00b0","F")
      print("The Setpoint is:    ", set_temp,u"\u00b0", "F")
      print("The Water flow is:  ",wtr_flow, "GPM")
      print("The Inlet? Temp is: ",inlet_temp,"",u"\u00b0", "F")
      print("The Unknown flow is:",unknown_flow, "  GPM")
    elif buf_list[5] == 42:
      # print("Gas Info Received!")
      print(" ")
      print("**************Gas Information***************")
      print(" ")
      total_gas_use = round(((buf_list[24]/10)*0.3508),2)
      low_byte_kcal = (buf_list[22])
      high_byte_kcal = (buf_list[23])
      total_kcal = high_byte_kcal + low_byte_kcal
      print("Total Gas Usage is: ", total_gas_use, "therm")
      print("Low Byte kCal is:   ", low_byte_kcal, "kCal")
      print("High Byte kCal is:  ", high_byte_kcal, "kCal")
      print("Total kCal:         ", total_kcal, "kCal")
    else:
      pass
      print("Bad Packet")
    time.sleep(1)
  except:
    pass

This was successful enough to produce the following output.

*************Water Information**************
 
The Power is:        On
The Outlet Temp is:  63.5  ° F
The Setpoint is:     140.0 ° F
The Water flow is:   0.0 GPM
The Inlet? Temp is:  68.9  ° F
The Unknown flow is: 0   GPM
 
**************Gas Information***************
 
Total Gas Usage is:  2.17 therm
Low Byte kCal is:    0 kCal
High Byte kCal is:   0 kCal
Total kCal:          0 kCal

So having this information I went towards my final goal of using ESPHome to create this similar interface. Below is the ESPHome Config.


# 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


esphome:
  name: navien
  friendly_name: Navien Water Heater
  includes:
    - uart_read_line_sensor.h

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:
  level: NONE #makes uart stream available in esphome logstream
  baud_rate: 0 #disable logging over uart

# Enable Home Assistant API
api:
  encryption:
    key: "redacted"

ota:
  password: "redacted"

substitutions:
  comment: "redacted"
  friendly_name: Navien Water Heater

globals:
- id: byte_timer
  type: int
  initial_value: "1"

interval:
- interval: 5s
  then:
    - lambda: |-
        id(byte_timer) = (id(byte_timer) + 1);
        if (id(byte_timer) > 25) {
          id(byte_timer) = 1;
        }

# Example configuration entry
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: false
    after:
      delimiter: "\n"
    sequence:
      - lambda: |-
          UARTDebug::log_string(direction, bytes);
          //looking at this string f7 05 50 50 90 22 42
          //where the 22 represents water information to follow
          //and in the same byte if the number is 34 it is 
          //representative of gas information
          if (bytes[0] == 247 && bytes[5] == 34) {
          //id(uart_byte0).publish_state(bytes[0]);
          //id(uart_byte1).publish_state(bytes[1]);
          //id(uart_byte2).publish_state(bytes[2]);
          //id(uart_byte3).publish_state(bytes[3]);
          //id(uart_byte4).publish_state(bytes[4]);
          //id(uart_byte5).publish_state(bytes[5]);
          //id(uart_byte6).publish_state(bytes[6]);
          //id(uart_byte7).publish_state(bytes[7]);
          //id(uart_byte8).publish_state(bytes[8]);
          //id(uart_byte9).publish_state(bytes[9]);
          //id(uart_byte10).publish_state(bytes[10]);
          id(set_temp).publish_state(((bytes[11]/2)*9/5) + 32);
          id(outlet_temp).publish_state(((bytes[12]/2)*9/5) + 32);
          id(inlet_temp).publish_state(((bytes[13]/2)*9/5) + 32);
          //id(uart_byte14).publish_state(bytes[14]);
          //id(uart_byte15).publish_state(bytes[15]);
          //id(uart_byte16).publish_state(bytes[16]);
          //id(uart_byte17).publish_state(bytes[17]);
          id(water_flow).publish_state((bytes[18]/10)* 0.2642);
          //id(uart_byte19).publish_state(bytes[19]);
          //id(uart_byte20).publish_state(bytes[20]);
          //id(uart_byte21).publish_state(bytes[21]);
          //id(uart_byte22).publish_state(bytes[22]);
          //id(uart_byte23).publish_state(bytes[23]);
          //id(uart_byte24).publish_state(bytes[24]);
          } else if (bytes[0] == 247 && bytes[5] == 42) {
          id(low_byte_kcal).publish_state(bytes[22]);
          id(high_byte_kcal).publish_state(bytes[23]);
          id(total_gas_use).publish_state((bytes[24]/10)*0.3508);
          } else (bytes[0]  != 247); {
          }
# Example configuration entry
switch:
  - platform: template
    name: "HW Heater On/Off"
    id: navien_switch
    restore_mode: ALWAYS_ON
    lambda: |-
      if ((id(navien_switch).state) != (id(navien_switch).state)) {
        return true;
      } else {
        return 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);


text_sensor:
- platform: custom
  lambda: |-
    auto my_custom_sensor = new UartReadLineSensor(id(uart_bus));
    App.register_component(my_custom_sensor);
    return {my_custom_sensor};
  text_sensors:
    id: "uart_readline"

text:
  - platform: template
    name: "Temp Set - Write"
    optimistic: true
    min_length: 0
    max_length: 5
    mode: text
    on_value:
      then:
        - sensor.template.publish:
            id: num_from_text
            state: !lambda |-
              auto n = parse_number<float>(x);
              return n.has_value() ? n.value() : NAN;

sensor:
  # - platform: template
  #   name: "Byte 0"
  #   id: uart_byte0
  # - platform: template
  #   name: "Byte 1"
  #   id: uart_byte1
  # - platform: template
  #   name: "Byte 2"
  #   id: uart_byte2
  # - platform: template
  #   name: "Byte 3"
  #   id: uart_byte3
  # - platform: template
  #   name: "Byte 4"
  #   id: uart_byte4
  # - platform: template
  #   name: "Byte 5"
  #   id: uart_byte5
  # - platform: template
  #   name: "Byte 6"
  #   id: uart_byte6
  # - platform: template
  #   name: "Byte 7"
  #   id: uart_byte7
  # - platform: template
  #   name: "Byte 8"
  #   id: uart_byte8
  # - platform: template
  #   name: "Byte 9"
  #   id: uart_byte9
  # - platform: template
  #   name: "Byte 10"
  #   id: uart_byte10
  - platform: template
    name: "Temp Set - Read"
    id: set_temp
    unit_of_measurement: °F
  - platform: template
    name: "Temp Outlet"
    id: outlet_temp
    unit_of_measurement: °F
    accuracy_decimals: 2
  - platform: template
    name: "Temp Inlet"
    id: inlet_temp
    unit_of_measurement: °F
  # - platform: template
  #   name: "Byte 14"
  #   id: uart_byte14
  # - platform: template
  #   name: "Byte 15"
  #   id: uart_byte15
  # - platform: template
  #   name: "Byte 16"
  #   id: uart_byte16
  # - platform: template
  #   name: "Byte 17"
  #   id: uart_byte17
  - platform: template
    name: "Water Flow"
    id: water_flow
    unit_of_measurement: GPM
  # - platform: template
  #   name: "Byte 19"
  #   id: uart_byte19
  # - platform: template
  #   name: "Byte 20"
  #   id: uart_byte20
  # - platform: template
  #   name: "Byte 21"
  #   id: uart_byte21
  # - platform: template
  #   name: "Byte 22"
  #   id: uart_byte22
  # - platform: template
  #   name: "Byte 23"
  #   id: uart_byte23
  # - platform: template
  #   name: "Byte 24"
  #   id: uart_byte24
  - platform: template
    name: "Total Gas Use"
    id: total_gas_use
    unit_of_measurement: therms
    accuracy_decimals: 2
  - platform: template
    name: "kCal Low Byte"
    id: low_byte_kcal
    unit_of_measurement: kCal
  - platform: template
    name: "kCal High Byte"
    id: high_byte_kcal
    unit_of_measurement: kCal
  - platform: template
    id: num_from_text
    name: "Temp Set - Write"
    internal: True

This seems to produce similar results as what I was seeing with the python code.

It looks like this:

I’m struggling with a few things here that I could use some tips on.

  1. When using the switch I created in ESPHome to turn on/off the Heater, ideally I’d like the switch to set it’s position based on the current status of the unit, then if the switch changes - send the appropriate command. Last, I’d like to know that the command was received and that it has gone into that mode.

  2. Similar question regarding the setpoint. In my config, I created a second “write” setpoint to try and begin configuring this function - but I’m not sure where to go from here.

In both of those cases, I’d like to have a single value/entity show on the ESPHome web page or entity in HA.

Any and all feedback is appreciated if you see something else here that would make this project better!

Hope this helps someone as they dive into their journey of doing the same!

3 Likes

Excitedly watching this from the sidelines! I have a NPE-240A2 that I’d love to monitor and control via HASS, ideally without the dreadful NaviLink experiences detailed above (and elsewhere on the web).

I’m a beginner with microcontrollers and ESPHome. Might poke around a bit with the above config and an 8266; still very much in the learning phase though.

@tsquared: Your code for uart_read_line_sensor.h appears to be python2. Curious if there’s a reason for this, since both MicroPython and ESPHome support python3? I’m happy to refactor it for python3 and implement a few best practices, unless there’s a technical dependency on py2.

What interface did you use to communicate over rs485 with the Navien heater? I have an NPE240A and would love to ditch the Navilink in favor of a local ESP32 solution! Thank you so much for sharing your work!

Hello @brystmar! To be brutally honest, I am not a python or C programmer by any means. I have been using google, and just searching and patching lines of code together, and think I did ok to get here. I am confident there is a more elegant solution for all of this. :smile: So - certainly if you know of a better/different way - I am all ears!

As it relates to the 8266 and doing this - there was a reason I used the ESP32, and I think it had to do with the number of UART’s on it, but I don’t specifically recall what I read that pointed me in that direction.

To answer your question regarding the uart_readline_sensor.h:

Not knowing how to program in C, or utilize the lambdas well enough to get the information out of the ESP32 that I have connected to the Navien, I utilized the Custom UART Text sensor listed here. Then, I utilized pieces of the code found in this thread. And coupled it with information found in this thread to get as far as I have. Again, Certainly not elegant, or likely efficient - but none-the-less, a semi-working copy!

You are more than welcome to refactor, or help with further development on this as I am sure we could all enjoy not having to use the Navilink Solution. Please let me know if I can provide more information.