Juntek KG-F Series on HA

A and B directly to ESP Tx and Rx without this?:

What to connect to Tx, A or B?

I just purchased the NB114 just because this post. Can you please show the internal settings of the NB114? Thanks.

Now that I have everything set, can’t find an RJ9 cable :joy::joy::joy:

Just for community awareness, I have wrote a fork that is now working very well for my Juntek. I have mine on a RPi, and works on both Bluteooth and RS485.
However, I can REALLY recommend you go through the RS485, as Bluteooth is plain bad (random value broadcast, no consistency, constant disconnect issues).
The RS485 however is accurate, consistent and easy.
I used one of those USB to RS485 from Amazon for about $10. It then pushes the data to MQQT.

Project and details available on the github : GitHub - AnalogThinker/junctek_monitor: A Python program to pull Juntek Battery Monitor Data over BT or RS485 and post on MQTT

Hello everyone!

I’ve recently developed an application that can read data from Junctek devices and transmit it over a port. The real-time data is stored as JSON and can be viewed through a visual dashboard available at this link: Junctek Monitoring Dashboard. This link shows my own battery online.

The system ultimately saves the data in a JSON file, making it potentially integrable with Home Assistant or other home automation systems. That integration is my next step.

As for the hardware connection, I used an RS485 cable that I had to fabricate myself – apologies for the DIY nature, but it was the quickest solution. Since I didn’t have an RJ9 connector available, I ended up using an old telephone cord which I modified to fit.

I will continue to develop and refine this project, and I’m excited to see how it can be expanded and integrated into more complex systems. Your feedback and suggestions are very welcome!

Looking forward to your thoughts and any advice you might have.

3 Likes

I’ve been checking your post out, and also been looking for ways to integrate python into HA. I know you have yo place the files into certain folders, but calling the results of the sensors is my issue. I’ve been using HA for 2 month now and I’m a NooB. :joy::joy::joy:

You don’t have to call the sensor’s results. Your sensor values will be pushed.
This is how MQTT works. Make sure your HA has the Mosquito MQTT add-on installed, running and configured (check tutorials if need to). Then as soon as MQTT gets sensor published, it will detect a new device and you will have a notification from HA on what to do with those.

As for my github scripts, I did not install anything on the HAOS, I am using a RPi with the USB-RS485 on the same network, that’s where it all happens, then it publishes to MQTT.

1 Like

How did you connect the KG-F to your RPi with USB to RS485? Can you share a picture?

As straight forward as it can be: I did connect the adapter in the RPi USB port.
And the KG-F connects to the RS485 side of the adapter.

Screenshot 2024-05-09 200223

Maaan, I have an RS485 to USB adapter in a drawer for the last 4 years and never thought of using it for this. :joy::joy::joy:. Thanks for the tip.

Note: Also remember that the Juntek does NOT play well with both the Display and the RS485 connected at the same time. If you need to do any adjustment on the device, disconnect the RS485 first, then connect the display. Same when done, disconnect the display first then connect the RS485 back.
I have not wasted time diving into the why, probably an address conflict, but we get what we pay for. And truly, it has been doing a great job for my HA integration so far that I don’t need the display anymore.

2 Likes

I have spent lot of time researching, and so on how da hell connect this device with ESP32… read all topics and so on… All of them talked about RS485, and so on so i bougth it but it did not had automatic flow control… in short here is “my” ESP HOME component GitHub - Tommixoft/esphome-junctek_kgf: Component for esphome to read status from a Junctek KG-F coulometer battery monitor via UART with NO rs485 modules.

Have all possible sensors from this device DONT NEED any rs485. So just connect your ESP32 rx tx ground to 4pin connector on Junctek. Install this component via ESPHOME. And here you go. All sensors in your HA (or not all if you dont need some you can remove them from esp config)

PS. Monitor have to be connected! This integration only READ data not write so monitor should stay connected. So you have best of both worlds, local monitor on battery pack and data in HA.

2 Likes

@Tommixoft
It seems so far the easiest and cleanest solution. One thing is missing in descriptions is pins used for RX TX. No voltage dividers?
I suppose A= RX B=TX or it is other way around? I might be wrong but isn’t rs485 pins 5V level?
I think I will try to connect Esp32 board inside the junctek box.

1 Like

Well, it’s alive…
Level of RS485 is at 3.3V so no divider needed.
I used ESP32 C3 super mini, since there was no easy way to route wires to RJ10 port solder points in main box, I used display enclosure to put esp module.


There is marked points for 5V and GND, RX TX is connected on other side to RJ10 port. Unfortunately I forgot to check which goes to A or B since it worked on first try, I guess it doesn’t matter much since pins can be changed in config anyway.

Thanks to @Tommixoft for hard part with code! With this it is really easy and clean way to integrate Junctek to HA
One note on config, for loging I had to comment out baud_rate, it is not allowing to leave 0

The other thing is values with Battery Ah Charged Total and Battery Ah Used Total in Ah is wrong. It should be Energy Charged Total KW and Energy Discharged Total.
Numbers are correct just for example it is showing 50.5Ah but it should be something like 505W or 0.505KW

1 Like

Hello,
I tired different solutions (informations are quite incomplete and contradictory), but the only one that worked for me is to use Tasmota SerialBridge (connecting directly EPS32 GPIOs to battery monitor).
No way to use EspHome.
I have also display connected.
Used a NodeMCU ESP32 (AZ-Delivery):

RS485 Link connections (without any RS485 adapter, just TTL):

  • A → GPIO16 (RX) (Green)
  • B → GPIO17 (TX) (Yellow)
  • GND → GND (Red)



On Tasmota:
firefox_2024-11-08_11-16-14
firefox_2024-11-08_11-17-11

Configured Baudrate, limiting MQTT publish messages rates (skip 26 messages), removing non ASCII chars
Backlog SBaudRate 115200; SetOption35 26; SerialDelimiter 128

10:42:39.680 CMD: Backlog SBaudRate 115200; SetOption35 26; SerialDelimiter 128
10:42:39.706 MQT: stat/Junctek_Monitor/RESULT = {"SBaudrate":115200}
10:42:39.933 MQT: stat/Junctek_Monitor/RESULT = {"SetOption35":26}
10:42:40.131 MQT: stat/Junctek_Monitor/RESULT = {"SerialDelimiter":128}
10:42:42.100 MQT: tele/Junctek_Monitor/RESULT = {"SSerialReceived":":r51=1,227,0,0,0,0,0,255,0,30,2800,100,100,100,0,0,1,100,0,0,0,20,20,255,0,0,0,15,"}
10:42:45.601 MQT: tele/Junctek_Monitor/RESULT = {"SSerialReceived":":R51=1,"}
10:42:48.982 MQT: tele/Junctek_Monitor/RESULT = {"SSerialReceived":":r50=1,87,5379,3793,104422,16683359,17074841,999999,121,0,99,1,274,100,241109,110504,"}
10:42:52.470 MQT: tele/Junctek_Monitor/RESULT = {"SSerialReceived":":R50=1,"}
10:42:55.615 MQT: tele/Junctek_Monitor/RESULT = {"SSerialReceived":":r51=1,227,0,0,0,0,0,255,0,30,2800,100,100,100,0,0,1,100,0,0,0,20,20,255,0,0,0,15,"}
10:42:59.120 MQT: tele/Junctek_Monitor/RESULT = {"SSerialReceived":":R51=1,"}
10:43:02.500 MQT: tele/Junctek_Monitor/RESULT = {"SSerialReceived":":r50=1,55,5379,3821,104570,16683359,17075638,999999,121,0,99,1,275,100,241109,110518,"}

Then on HomeAssistant I split values using automations and publish new MQTT messages for each record 50 value (only if checksum matches):

alias: Battery Monitor split data 50
description: "Split JuncTek message r50 record (live measures)"
triggers:
  - trigger: mqtt
    topic: tele/Junctek_Monitor/RESULT
    payload: "True"
    value_template: "{{ value_json.SSerialReceived.split('=')[0] == ':r50'}}"
    variables:
      split_values: "{{ trigger.payload_json.SSerialReceived.split('=')[1].split(',')[:16] }}"
      item:
        - id
        - checksum
        - voltage
        - current
        - remaining_capacity
        - discharging_energy
        - charge_energy
        - operational
        - temperature
        - function
        - output_status
        - direction
        - remaining_time
        - time_adjustment
        - date
        - time
      check_sum: >-
        {% set ns=namespace(sum0=0) %} 
        {% for i in range(2, 16) %}
           {% set ns.sum0 = ns.sum0 + int(split_values[i]) %}
        {% endfor %} 
		{% set cs = ns.sum0 % 255 + 1 %} 
		{% if int(cs) == int(split_values[1]) %}
          {{ cs }}
        {% endif %}
conditions:
  - condition: template
    value_template: "{{ check_sum == int(split_values[1]) }}"
actions:
  - repeat:
      count: "{{ item | length }}"
      sequence:
        - data:
            topic: tele/bm/{{item[repeat.index-1]}}
            payload: "{{split_values[repeat.index-1]}}"
            retain: true
          action: mqtt.publish
mode: single

and a message for record 51 (total capacity)

alias: Battery Monitor split data 51
description: "Split JuncTek message r51 record (settings values)"
triggers:
  - trigger: mqtt
    topic: tele/Junctek_Monitor/RESULT
    payload: "True"
    value_template: "{{ value_json.SSerialReceived.split('=')[0] == ':r51'}}"
    variables:
      split_values: "{{ trigger.payload_json.SSerialReceived.split('=')[1].split(',')[:28] }}"
      check_sum: >-
        {% set x = trigger.payload_json.SSerialReceived.split('=')[1].split(',')[:28] %} 
        {% set ns=namespace(sum0=0) %} 
        {% for i in range(2, 28) %}
           {% set ns.sum0 = ns.sum0 + int(x[i]) %}
        {% endfor %} 
        {% set cs = ns.sum0 % 255 + 1 %} 
        {% if int(cs) == int(x[1]) %}
          {{ cs }}
        {% endif %}
conditions:
  - condition: template
    value_template: "{{ check_sum == int(split_values[1]) }}"
actions:
  - action: mqtt.publish
    metadata: {}
    data:
      evaluate_payload: false
      qos: 0
      retain: true
      topic: tele/bm/capacity
      payload: "{{ split_values[10] }}"
mode: single

Then create sensors from MQTT

- sensor:
    - name: "Voltage"
      unique_id: junctek_kh140f_voltage
      state_topic: "tele/bm/voltage"
      value_template: "{{ (value | float() / 100) | round(1) }}"
      unit_of_measurement: "V"
      device_class: voltage
      device:
        name: "Battery Monitor"
        identifiers: "239400662"
        manufacturer: Junctek
        model: "KH140F"

    - name: "Current"
      unique_id: junctek_kh140f_current
      state_topic: "tele/bm/current"
      value_template: |-
            {% set ns=namespace(fact = 1) %}
            {% if is_state('binary_sensor.battery_monitor_discharging', 'on') %}
                  {% set ns.fact = -1 %}
            {% endif %}
            {{ ns.fact * (value | float() / 100)|round(1) }}
      unit_of_measurement: "A"
      device_class: current
      device:
        name: "Battery Monitor"
        identifiers: "239400662"
        manufacturer: Junctek
        model: "KH140F"

    - name: "Remaining Capcity"
      unique_id: junctek_kh140f_remaining_capacity
      state_topic: "tele/bm/remaining_capacity"
      value_template: "{{ (value | float() / 1000) | round(1) }}"
      unit_of_measurement: "Ah"
      device_class: energy_storage
      device:
        name: "Battery Monitor"
        identifiers: "239400662"
        manufacturer: Junctek
        model: "KH140F"

    - name: "Discharge Energy"
      unique_id: junctek_kh140f_discharging_energy
      state_topic: "tele/bm/discharging_energy"
      value_template: |-
              {{ (value | float() / 100000)|round(2) }}
      unit_of_measurement: "kWh"
      device_class: energy
      device:
        name: "Battery Monitor"
        identifiers: "239400662"
        manufacturer: Junctek
        model: "KH140F"

    - name: "Charge Energy"
      unique_id: junctek_kh140f_charge_energy
      state_topic: "tele/bm/charge_energy"
      value_template: |-
              {{ (value | float() / 100000)|round(2) }}
      unit_of_measurement: "kWh"
      device_class: energy
      device:
        name: "Battery Monitor"
        identifiers: "239400662"
        manufacturer: Junctek
        model: "KH140F"

    - name: "Temperature"
      unique_id: junctek_kh140f_temperature
      state_topic: "tele/bm/temperature"
      value_template: |-
              {{ (value | int) - 100 }}
      unit_of_measurement: "°C"
      device_class: temperature
      device:
        name: "Battery Monitor"
        identifiers: "239400662"
        manufacturer: Junctek
        model: "KH140F"

    - name: "Output Status"
      unique_id: junctek_kh140f_output_status
      state_topic: "tele/bm/output_status"
      value_template: |-
               {% set status_code = (value | int) %} 
               {{ { 0: 'ON',
                    1: 'OVP',
                    2: 'OCP', 
                    3: 'LVP',
                    4: 'NCP',
                    5: 'OPP',
                    6: 'OTP', 
                   99: 'OFF'}.get(status_code|int(0), 'unknown') }}
      device:
        name: "Battery Monitor"
        identifiers: "239400662"
        manufacturer: Junctek
        model: "KH140F"

    - name: "Power"
      unique_id: junctek_kh140f_power
      state_topic: "tele/bm/current"
      value_template: |-
            {% set volt = states('sensor.battery_monitor_voltage', rounded=False, with_unit=False)|float %}
            {% set amp =  states('sensor.battery_monitor_current', rounded=False, with_unit=False)|float %}
            {{ (volt * amp) | round(2) }}
      unit_of_measurement: "W"
      device_class: power
      device:
        name: "Battery Monitor"
        identifiers: "239400662"
        manufacturer: Junctek
        model: "KH140F"
        
    - name: "SoC"
      unique_id: junctek_kh140f_soc
      state_topic: "tele/bm/remaining_capacity"
      value_template: |-
            {% set capacity = (states('sensor.battery_monitor_total_capacity', rounded=False, with_unit=False) | float) %}
            {% set remain = (states('sensor.battery_monitor_remaining_capacity', rounded=False, with_unit=False) | float) %}
            {% if remain <= capacity %}
                  {% set soc = ((remain / capacity) * 100) | round(2) %}
                  {{ soc }}
            {% endif %}
      unit_of_measurement: "%"
      device_class: battery
      device:
        name: "Battery Monitor"
        identifiers: "239400662"
        manufacturer: Junctek
        model: "KH140F"

    - name: "Remaining Time"
      unique_id: junctek_kh140f_remaining_time
      state_topic: "tele/bm/remaining_time"
      value_template: |-
                {{ (value | float() / 60) | round(2) }}
      unit_of_measurement: "h"
      device_class: duration
      device:
        name: "Battery Monitor"
        identifiers: "239400662"
        manufacturer: Junctek
        model: "KH140F"

    - name: "Total Capacity"
      unique_id: junctek_kh140f_total_capacity
      state_topic: "tele/bm/capacity"
      value_template: |-
              {{ ((value | int / 10) | int) }} 
      unit_of_measurement: "Ah"
      device_class: energy_storage
      device:
        name: "Battery Monitor"
        identifiers: "239400662"
        manufacturer: Junctek
        model: "KH140F"
        
- binary_sensor:        
    - name: "Charging"
      unique_id: junctek_kh140f_charging
      state_topic: "tele/bm/direction"
      value_template: |-
               {% set direction = (value |  int) %}
               {% if direction == 1 %}
                  {{ "ON" }}
               {% else %}
                  {{ "OFF" }}
               {% endif %}
      payload_on: "ON"
      device:
        name: "Battery Monitor"
        identifiers: "239400662"
        manufacturer: Junctek
        model: "KH140F"

    - name: "Discharging"
      unique_id: junctek_kh140f_discharging
      state_topic: "tele/bm/direction"
      value_template: |-
               {% set direction = (value |  int) %}
               {% if direction == 1 %}
                  {{ "OFF" }}
               {% else %}
                  {{ "ON" }}
               {% endif %}
      payload_on: "ON"
      device:
        name: "Battery Monitor"
        identifiers: "239400662"
        manufacturer: Junctek
        model: "KH140F"

I will try @Tommixoft solution too

EDIT:
I tried @Tommixoft solution, it works more or less.
For KHF devices data is a little bit different.
And as said by @incar there are some wrong unit of measure used (Ah instead of kWh, etc) for charging/discharging energy and power.

I forked project and made some adjustements
GitHub junctek_khf

1 Like

Thanks @pongo I will try your version. Not that it is very important but is there any chance and way to expand functionality for output status state. Currently 0 or 99 is not that easy readable.

Sure, I will try to put map to something more meaningful

Great!
How did you get 16 sensors? I still have 13 using your version.

Sensor converted in a text_sensor.

Energy Today are 2 meters configured on HA.
Uptime is

  - platform: uptime
    name: "${name} Uptime"
    id: ${device_id}_uptime_sensor

I also updated yaml exmaple

substitutions:
  name: "junctek-kh140f"
  device_name: ${name}
  device_id: junctek_kh140f
  friendly_name: "Junctek KH140F"
  device_description: "Monitor Junctek KH140F SmartShunt via RS485 Link in TTL mode"

esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  comment: ${device_description}

external_components:
  - source: github://gianfrdp/esphome-junctek_khf@junctek_khf
    components: [ junctek_khf ]

esp32:
  board: esp32dev
  framework:
    type: arduino

uart:
  tx_pin: GPIO17
  rx_pin: GPIO16
  id: uart_junctek
  baud_rate: 115200

junctek_khf:
  id: junctek_id
  address: 1
  invert_current: false
  update_stats_interval: 5000 # 5 seconds
  update_settings_interval: 30000 # 30 seconds
  uart_id: uart_junctek

sensor:
  - platform: junctek_khf
    junctek_id: junctek_id
    voltage:
      name: "${name} Voltage"
      id: ${device_id}_voltage
    current:
      name: "${name} Current"
      id: ${device_id}_current
    battery_level:
      name: "${name} SoC"
      id: ${device_id}_battery_level
    power:
      name: "${name} Power"
      id: ${device_id}_power
    amp_hour_remain:
      name: "${name} Remaining Capacity"
      id: ${device_id}_remaining_capacity
    battery_capacity:
      name: "${name} Total Capacity"
      id: ${device_id}_battery_capacity
    energy_charged:
      name: "${name} Charging Energy"
      id: ${device_id}_charging_energy
    energy_discharged:
      name: "${name} Discharging Energy"
      id: ${device_id}_discharging_energy
    temperature:
      name: "${name} Temperature"
      id: ${device_id}_temperature
    remaining_time:
      name: "${name} Remaining Time"
      id: ${device_id}_remaining_time
    charging_power:
      name: "${name} Charging Power"
      id: ${device_id}_charging_power
    discharging_power:
      name: "${name} Discharging Power"   
      id: ${device_id}_discharging_power

text_sensor:
  - platform: junctek_khf
    junctek_id: junctek_id
    output_status:
      name: "${name} Output Status"
      id: ${device_id}_output_status

I’m going to add also other setting sensors

Cool!
One thing to sort out is with energy, Currently it is rounded to 0.1kWh, it would be really useful to have 0.001kWh or at least 0.01kWh. for smaller loads that would be make more sense.

I see that you greatly expanded config, but something is missing. I’m getting errors