Navien, ESP32 Navilink interface

I have a Navien Hot Water Heater, that uses RS-485 to communicate. What I am trying to build is an ESP32 Interface for this unit, and have quite a bit of it working. I am hoping to find that help here and allow me to finish out the last few items that will make this complete!. In a thread here, contains most of the details.

Below is my current Configuration and a snip from the subsequent web page it produces.

I am confident there are better/more efficient ways to produce this, but with my basic knowledge of cutting and pasting code together, I have managed to stumble my way along to get here. I am open to any and all help you and provide!

Thanks!


# 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

I’m struggling with a few things 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!

Following so I can learn too :slight_smile: