I am going to describe how to read Data from an iBMS board (Jamestronics iBMS) that aggregates and manages data from several different DC Home Battery packs. In my case, the battery backs all have Jikong BMS (better known as JK-BMS) PB inverter BMS boards.
The Bluetooth / ESP32 connection (see this discussion (thanks again @SRX for pointing me to the solution)) was a bit too unstable for my liking and I could already see that the ESP32 is struggling to get all data from three battery packs already. Plus, the BMS boards annoyingly beep whenever data is being read out, so that was a constant thorn in my side I wanted to get rid off). Thirdly, the bluetooth connection is completely unencrypted and unsecured (client side PIN Jikong? wtf?), so I was really itching to switch off bluetooth sooner rather than later.
In comes the iBMS board from Uksa007. This board offers a wired RS485 connection into the native inter BMS communication and is able to monitor up to 16 JK-BMS of my specific type with a single board. It offers an api that aggregates all the measurements in a structured JSON data block that can easily be parsed using the http binding.
Here’s what the JSON looks like in my case:
{
"device": "iBMS-Master",
"board_type": 1,
"options": 1,
"role": "Master",
"bms_num": 0,
"bms_type": "JK-INV",
"code_version": "3.481",
"chip_id": 145272580283232,
"led_state": 1,
"uptime": "0d 21h 6m 48s",
"charge_status": "Float",
"can_protocol": "Victron",
"charge_enabled": "ON",
"discharge_enabled": "ON",
"absorption_voltage": 55.2,
"absorption_time": 15,
"rebulk_offset": 1.6,
"float_mode": "ON",
"float_time": 10,
"manual_charge": "OFF",
"force_chg_v": 41.6,
"emergency_discharge_on_alarm": "OFF",
"slow_charge": "OFF",
"slow_charge_voltage_1": 54.1,
"slow_charge_voltage_2": 54.3,
"slow_charge_voltage_3": 54.5,
"slow_charge_voltage_4": 54.7,
"slow_charge_voltage_5": 54.9,
"slow_charge_voltage_6": 55.1,
"slow_charge_current_1": 80,
"slow_charge_current_1": 80,
"slow_charge_current_2": 40,
"slow_charge_current_3": 20,
"slow_charge_current_4": 10,
"slow_charge_current_5": 5,
"slow_charge_current_6": 2,
"slow_charge_timeout_max": 300,
"slaves_total": 0,
"slaves_detected": 0,
"combined_voltage": 52.92,
"combined_current": -32.18,
"combined_power": -1703,
"combined_soc": 86,
"combined_soh": 100,
"combined_cvl": 53.6,
"combined_ccl": 285,
"combined_dcl": 285,
"combined_C_Engery": 55.6279127687636,
"combined_D_Engery": 33.486549345386976,
"absorption_time_remain": 0,
"float_time_remain": 4.18,
"charge_current": 285,
"discharge_current": 285,
"min_discharge_v": 42.4,
"capacity_remaining": 86,
"calculated_capacity_remaining": 0,
"battery_soh": 100,
"logs": "OFF",
"errors": "Good",
"errors_bitmask": 0,
"temperature_sensors": 0,
"temperature_sensor_1": 26.7,
"total_voltage": 52.92,
"current": -32.18,
"power": -1702.97,
"charging_power": 0,
"discharging_power": 1702.97,
"charged_energy": 55.6,
"discharged_energy": 33.5,
"total_battery_capacity_setting": 942,
"modules_online": 3,
"modules_offline": 0,
"batteries": [{
"id": 0,
"total_voltage": 52.89,
"current": -11.02,
"capacity_remaining": 86,
"power": -582.9,
"charging_power": 0,
"discharging_power": 582.9,
"charged_energy": 18.656853902544647,
"discharged_energy": 11.231567386991179,
"battery_soh": 100,
"min_voltage_cell": 2,
"max_voltage_cell": 1,
"min_cell_voltage": 3.305,
"max_cell_voltage": 3.307,
"delta_cell_voltage": 0.002,
"average_cell_voltage": 3.306,
"battery_strings": 16,
"cell_voltage_1": 3.307,
"cell_voltage_2": 3.305,
"cell_voltage_3": 3.307,
"cell_voltage_4": 3.307,
"cell_voltage_5": 3.307,
"cell_voltage_6": 3.305,
"cell_voltage_7": 3.305,
"cell_voltage_8": 3.307,
"cell_voltage_9": 3.305,
"cell_voltage_10": 3.307,
"cell_voltage_11": 3.307,
"cell_voltage_12": 3.307,
"cell_voltage_13": 3.305,
"cell_voltage_14": 3.307,
"cell_voltage_15": 3.305,
"cell_voltage_16": 3.305,
"power_tube_temperature": 24.7,
"temperature_sensors": 4,
"temperature_sensor_1": 25.1,
"temperature_sensor_2": 24.8,
"temperature_sensor_3": 24.9,
"temperature_sensor_4": 25.1,
"errors_bitmask": 0,
"total_battery_capacity_setting": 314,
"charging_cycles": 56,
"total_charging_cycle_capacity": 17616,
"capacity_remaining_derived": 271.13,
"balance_curent": 0,
"balancing": 0,
"charging_switch": 1,
"discharging_switch": 1,
"total_runtime": 17663953
}, {
"id": 1,
// snip, Battery Pack 1 looks exactly like above
}, {
"id": 2,
// snip, Battery Pack 2 looks exactly like above
}]
}
And here is the .things file using the http binding and some JSONPATH transformations:
Thing http:url:ibms "iBMS" [
baseURL="http://ip/api",
refresh=15,
timeout=5000,
contentType="application/json"
] {
Channels:
// =================================================
// ===== Controller / Master information ===========
// =================================================
Type string : device [ stateTransformation="JSONPATH:$.device" ]
Type number : board_type [ stateTransformation="JSONPATH:$.board_type" ]
Type number : options [ stateTransformation="JSONPATH:$.options" ]
Type string : role [ stateTransformation="JSONPATH:$.role" ]
Type number : bms_num [ stateTransformation="JSONPATH:$.bms_num" ]
Type string : bms_type [ stateTransformation="JSONPATH:$.bms_type" ]
Type string : code_version [ stateTransformation="JSONPATH:$.code_version" ]
Type number : chip_id [ stateTransformation="JSONPATH:$.chip_id" ]
Type number : led_state [ stateTransformation="JSONPATH:$.led_state" ]
Type string : uptime [ stateTransformation="JSONPATH:$.uptime" ]
Type string : charge_status [ stateTransformation="JSONPATH:$.charge_status" ]
Type string : can_protocol [ stateTransformation="JSONPATH:$.can_protocol" ]
Type string : charge_enabled [ stateTransformation="JSONPATH:$.charge_enabled" ]
Type string : discharge_enabled [ stateTransformation="JSONPATH:$.discharge_enabled" ]
Type string : logs [ stateTransformation="JSONPATH:$.logs" ]
Type string : errors [ stateTransformation="JSONPATH:$.errors" ]
Type number : errors_bitmask [ stateTransformation="JSONPATH:$.errors_bitmask" ]
// =================================================
// ===== Charging / control parameters =============
// =================================================
Type number : absorption_voltage [ stateTransformation="JSONPATH:$.absorption_voltage" ]
Type number : absorption_time [ stateTransformation="JSONPATH:$.absorption_time" ]
Type number : rebulk_offset [ stateTransformation="JSONPATH:$.rebulk_offset" ]
Type string : float_mode [ stateTransformation="JSONPATH:$.float_mode" ]
Type number : float_time [ stateTransformation="JSONPATH:$.float_time" ]
Type string : manual_charge [ stateTransformation="JSONPATH:$.manual_charge" ]
Type number : force_chg_v [ stateTransformation="JSONPATH:$.force_chg_v" ]
Type string : emergency_discharge_on_alarm
[ stateTransformation="JSONPATH:$.emergency_discharge_on_alarm" ]
Type string : slow_charge [ stateTransformation="JSONPATH:$.slow_charge" ]
// =================================================
// ===== Combined / aggregated values ==============
// =================================================
Type number : combined_voltage [ stateTransformation="JSONPATH:$.combined_voltage" ]
Type number : combined_current [ stateTransformation="JSONPATH:$.combined_current" ]
Type number : combined_power [ stateTransformation="JSONPATH:$.combined_power" ]
Type number : combined_soc [ stateTransformation="JSONPATH:$.combined_soc" ]
Type number : combined_soh [ stateTransformation="JSONPATH:$.combined_soh" ]
Type number : combined_cvl [ stateTransformation="JSONPATH:$.combined_cvl" ]
Type number : combined_ccl [ stateTransformation="JSONPATH:$.combined_ccl" ]
Type number : combined_dcl [ stateTransformation="JSONPATH:$.combined_dcl" ]
Type number : combined_C_Engery [ stateTransformation="JSONPATH:$.combined_C_Engery" ]
Type number : combined_D_Engery [ stateTransformation="JSONPATH:$.combined_D_Engery" ]
// =================================================
// ===== General measurements ======================
// =================================================
Type number : temperature_sensors [ stateTransformation="JSONPATH:$.temperature_sensors" ]
Type number : temperature_sensor_1 [ stateTransformation="JSONPATH:$.temperature_sensor_1" ]
Type number : total_voltage [ stateTransformation="JSONPATH:$.total_voltage" ]
Type number : current [ stateTransformation="JSONPATH:$.current" ]
Type number : power [ stateTransformation="JSONPATH:$.power" ]
Type number : charging_power [ stateTransformation="JSONPATH:$.charging_power" ]
Type number : discharging_power [ stateTransformation="JSONPATH:$.discharging_power" ]
Type number : charged_energy [ stateTransformation="JSONPATH:$.charged_energy" ]
Type number : discharged_energy [ stateTransformation="JSONPATH:$.discharged_energy" ]
Type number : total_battery_capacity_setting [ stateTransformation="JSONPATH:$.total_battery_capacity_setting" ]
Type number : modules_online [ stateTransformation="JSONPATH:$.modules_online" ]
Type number : modules_offline [ stateTransformation="JSONPATH:$.modules_offline" ]
// =================================================
// ===== Battery 0 (bms00) =========================
// =================================================
Type number : bms00_total_voltage [ stateTransformation="JSONPATH:$.batteries[0].total_voltage" ]
Type number : bms00_current [ stateTransformation="JSONPATH:$.batteries[0].current" ]
Type number : bms00_power [ stateTransformation="JSONPATH:$.batteries[0].power" ]
Type number : bms00_charging_power [ stateTransformation="JSONPATH:$.batteries[0].charging_power" ]
Type number : bms00_discharging_power [ stateTransformation="JSONPATH:$.batteries[0].discharging_power" ]
Type number : bms00_charged_energy [ stateTransformation="JSONPATH:$.batteries[0].charged_energy" ]
Type number : bms00_discharged_energy [ stateTransformation="JSONPATH:$.batteries[0].discharged_energy" ]
Type number : bms00_capacity_remaining [ stateTransformation="JSONPATH:$.batteries[0].capacity_remaining" ]
Type number : bms00_battery_soh [ stateTransformation="JSONPATH:$.batteries[0].battery_soh" ]
Type number : bms00_min_voltage_cell [ stateTransformation="JSONPATH:$.batteries[0].min_voltage_cell" ]
Type number : bms00_max_voltage_cell [ stateTransformation="JSONPATH:$.batteries[0].max_voltage_cell" ]
Type number : bms00_min_cell_voltage [ stateTransformation="JSONPATH:$.batteries[0].min_cell_voltage" ]
Type number : bms00_max_cell_voltage [ stateTransformation="JSONPATH:$.batteries[0].max_cell_voltage" ]
Type number : bms00_delta_cell_voltage [ stateTransformation="JSONPATH:$.batteries[0].delta_cell_voltage" ]
Type number : bms00_average_cell_voltage [ stateTransformation="JSONPATH:$.batteries[0].average_cell_voltage" ]
Type number : bms00_power_tube_temperature [ stateTransformation="JSONPATH:$.batteries[0].power_tube_temperature" ]
Type number : bms00_temperature_sensors [ stateTransformation="JSONPATH:$.batteries[0].temperature_sensors" ]
Type number : bms00_temperature_sensor_1 [ stateTransformation="JSONPATH:$.batteries[0].temperature_sensor_1" ]
Type number : bms00_temperature_sensor_2 [ stateTransformation="JSONPATH:$.batteries[0].temperature_sensor_2" ]
Type number : bms00_temperature_sensor_3 [ stateTransformation="JSONPATH:$.batteries[0].temperature_sensor_3" ]
Type number : bms00_temperature_sensor_4 [ stateTransformation="JSONPATH:$.batteries[0].temperature_sensor_4" ]
Type number : bms00_charging_cycles [ stateTransformation="JSONPATH:$.batteries[0].charging_cycles" ]
Type number : bms00_total_charging_cycle_capacity [ stateTransformation="JSONPATH:$.batteries[0].total_charging_cycle_capacity" ]
Type number : bms00_capacity_remaining_derived [ stateTransformation="JSONPATH:$.batteries[0].capacity_remaining_derived" ]
Type number : bms00_balance_curent [ stateTransformation="JSONPATH:$.batteries[0].balance_curent" ]
Type number : bms00_balancing [ stateTransformation="JSONPATH:$.batteries[0].balancing" ]
Type number : bms00_charging_switch [ stateTransformation="JSONPATH:$.batteries[0].charging_switch" ]
Type number : bms00_discharging_switch [ stateTransformation="JSONPATH:$.batteries[0].discharging_switch" ]
Type number : bms00_total_runtime [ stateTransformation="JSONPATH:$.batteries[0].total_runtime" ]
// ---- Cell voltages 1–16 (bms00) ----
Type number : bms00_cell_voltage_1 [ stateTransformation="JSONPATH:$.batteries[0].cell_voltage_1" ]
Type number : bms00_cell_voltage_2 [ stateTransformation="JSONPATH:$.batteries[0].cell_voltage_2" ]
Type number : bms00_cell_voltage_3 [ stateTransformation="JSONPATH:$.batteries[0].cell_voltage_3" ]
Type number : bms00_cell_voltage_4 [ stateTransformation="JSONPATH:$.batteries[0].cell_voltage_4" ]
Type number : bms00_cell_voltage_5 [ stateTransformation="JSONPATH:$.batteries[0].cell_voltage_5" ]
Type number : bms00_cell_voltage_6 [ stateTransformation="JSONPATH:$.batteries[0].cell_voltage_6" ]
Type number : bms00_cell_voltage_7 [ stateTransformation="JSONPATH:$.batteries[0].cell_voltage_7" ]
Type number : bms00_cell_voltage_8 [ stateTransformation="JSONPATH:$.batteries[0].cell_voltage_8" ]
Type number : bms00_cell_voltage_9 [ stateTransformation="JSONPATH:$.batteries[0].cell_voltage_9" ]
Type number : bms00_cell_voltage_10 [ stateTransformation="JSONPATH:$.batteries[0].cell_voltage_10" ]
Type number : bms00_cell_voltage_11 [ stateTransformation="JSONPATH:$.batteries[0].cell_voltage_11" ]
Type number : bms00_cell_voltage_12 [ stateTransformation="JSONPATH:$.batteries[0].cell_voltage_12" ]
Type number : bms00_cell_voltage_13 [ stateTransformation="JSONPATH:$.batteries[0].cell_voltage_13" ]
Type number : bms00_cell_voltage_14 [ stateTransformation="JSONPATH:$.batteries[0].cell_voltage_14" ]
Type number : bms00_cell_voltage_15 [ stateTransformation="JSONPATH:$.batteries[0].cell_voltage_15" ]
Type number : bms00_cell_voltage_16 [ stateTransformation="JSONPATH:$.batteries[0].cell_voltage_16" ]
// Battery ID 01 and 02 look the same as battery 00, just modify channel names and JSONPATH
}
Finally, the items:
Group jkbms
// ======================================================
// JK-BMS readout via iBMS HTTP API (UPDATED)
// ======================================================
// -------- Stack / combined --------
Number:ElectricPotential Stack_totalV
"Stack Voltage [%.3f V]" (jkbms)
{ channel="http:url:ibms:total_voltage" }
Number:ElectricCurrent Stack_current
"Stack total current [%.2f A]" (jkbms)
{ channel="http:url:ibms:current" }
Number:Power Stack_power
"Stack total power [%.1f W]" (jkbms)
{ channel="http:url:ibms:power" }
Number:Power Stack_charging_power
"Stack charging power [%.1f W]" (jkbms)
{ channel="http:url:ibms:charging_power" }
Number:Power Stack_discharging_power
"Stack discharging power [%.1f W]" (jkbms)
{ channel="http:url:ibms:discharging_power" }
Number:Energy Stack_charged_energy
"Stack charged energy [%.3f kWh]" (jkbms)
{ channel="http:url:ibms:charged_energy" }
Number:Energy Stack_discharged_energy
"Stack discharged energy [%.3f kWh]" (jkbms)
{ channel="http:url:ibms:discharged_energy" }
Number:ElectricCharge Stack_capacity_setting
"Stack Battery Capacity Setting [%.1f Ah]" (jkbms)
{ channel="http:url:ibms:total_battery_capacity_setting" }
// -------- BMS00 summary --------
Number:ElectricPotential BMS00_totalV
"BMS_00 Pack Voltage [%.3f V]" (jkbms)
{ channel="http:url:ibms:bms00_total_voltage" }
Number:ElectricCurrent BMS00_current
"BMS_00 current [%.2f A]" (jkbms)
{ channel="http:url:ibms:bms00_current" }
Number:Power BMS00_power
"BMS_00 power [%.1f W]" (jkbms)
{ channel="http:url:ibms:bms00_power" }
Number:Power BMS00_charging_power
"BMS_00 charging power [%.1f W]" (jkbms)
{ channel="http:url:ibms:bms00_charging_power" }
Number:Power BMS00_discharging_power
"BMS_00 discharging power [%.1f W]" (jkbms)
{ channel="http:url:ibms:bms00_discharging_power" }
Number:Energy BMS00_charged_energy
"BMS_00 charged energy [%.3f kWh]" (jkbms)
{ channel="http:url:ibms:bms00_charged_energy" }
Number:Energy BMS00_discharged_energy
"BMS_00 discharged energy [%.3f kWh]" (jkbms)
{ channel="http:url:ibms:bms00_discharged_energy" }
Number:Dimensionless BMS00_capacity_remaining
"BMS_00 capacity remaining [%.1f %%]" (jkbms)
{ channel="http:url:ibms:bms00_capacity_remaining" }
// -------- Cell voltage statistics --------
// ---- Min/Max cell ID (index) ----
Number:Dimensionless BMS00_minCellID "BMS_00 min Voltage Cell ID [%.0f]" (jkbms) { channel="http:url:ibms:bms00_min_voltage_cell" }
Number:Dimensionless BMS00_maxCellID "BMS_00 max Voltage Cell ID [%.0f]" (jkbms) { channel="http:url:ibms:bms00_max_voltage_cell" }
Number:ElectricPotential BMS00_minCellV
"BMS_00 min Cell Voltage [%.3f V]" (jkbms)
{ channel="http:url:ibms:bms00_min_cell_voltage" }
Number:ElectricPotential BMS00_maxCellV
"BMS_00 max Cell Voltage [%.3f V]" (jkbms)
{ channel="http:url:ibms:bms00_max_cell_voltage" }
Number:ElectricPotential BMS00_deltaCellV
"BMS_00 delta Cell Voltage [%.3f V]" (jkbms)
{ channel="http:url:ibms:bms00_delta_cell_voltage" }
Number:ElectricPotential BMS00_averageCellV
"BMS_00 average Cell Voltage [%.3f V]" (jkbms)
{ channel="http:url:ibms:bms00_average_cell_voltage" }
// -------- Individual cell voltages --------
Number:ElectricPotential BMS00_C1 "BMS_00 Cell 1 [%.3f V]" (jkbms) { channel="http:url:ibms:bms00_cell_voltage_1" }
Number:ElectricPotential BMS00_C2 "BMS_00 Cell 2 [%.3f V]" (jkbms) { channel="http:url:ibms:bms00_cell_voltage_2" }
Number:ElectricPotential BMS00_C3 "BMS_00 Cell 3 [%.3f V]" (jkbms) { channel="http:url:ibms:bms00_cell_voltage_3" }
Number:ElectricPotential BMS00_C4 "BMS_00 Cell 4 [%.3f V]" (jkbms) { channel="http:url:ibms:bms00_cell_voltage_4" }
Number:ElectricPotential BMS00_C5 "BMS_00 Cell 5 [%.3f V]" (jkbms) { channel="http:url:ibms:bms00_cell_voltage_5" }
Number:ElectricPotential BMS00_C6 "BMS_00 Cell 6 [%.3f V]" (jkbms) { channel="http:url:ibms:bms00_cell_voltage_6" }
Number:ElectricPotential BMS00_C7 "BMS_00 Cell 7 [%.3f V]" (jkbms) { channel="http:url:ibms:bms00_cell_voltage_7" }
Number:ElectricPotential BMS00_C8 "BMS_00 Cell 8 [%.3f V]" (jkbms) { channel="http:url:ibms:bms00_cell_voltage_8" }
Number:ElectricPotential BMS00_C9 "BMS_00 Cell 9 [%.3f V]" (jkbms) { channel="http:url:ibms:bms00_cell_voltage_9" }
Number:ElectricPotential BMS00_C10 "BMS_00 Cell 10 [%.3f V]" (jkbms) { channel="http:url:ibms:bms00_cell_voltage_10" }
Number:ElectricPotential BMS00_C11 "BMS_00 Cell 11 [%.3f V]" (jkbms) { channel="http:url:ibms:bms00_cell_voltage_11" }
Number:ElectricPotential BMS00_C12 "BMS_00 Cell 12 [%.3f V]" (jkbms) { channel="http:url:ibms:bms00_cell_voltage_12" }
Number:ElectricPotential BMS00_C13 "BMS_00 Cell 13 [%.3f V]" (jkbms) { channel="http:url:ibms:bms00_cell_voltage_13" }
Number:ElectricPotential BMS00_C14 "BMS_00 Cell 14 [%.3f V]" (jkbms) { channel="http:url:ibms:bms00_cell_voltage_14" }
Number:ElectricPotential BMS00_C15 "BMS_00 Cell 15 [%.3f V]" (jkbms) { channel="http:url:ibms:bms00_cell_voltage_15" }
Number:ElectricPotential BMS00_C16 "BMS_00 Cell 16 [%.3f V]" (jkbms) { channel="http:url:ibms:bms00_cell_voltage_16" }
// -------- Temperatures --------
Number:Temperature BMS00_power_tube_temperature
"BMS_00 power tube temperature [%.1f °C]" (jkbms)
{ channel="http:url:ibms:bms00_power_tube_temperature" }
Number:Temperature BMS00_T1 "BMS_00 temperature T1 [%.1f °C]" (jkbms) { channel="http:url:ibms:bms00_temperature_sensor_1" }
Number:Temperature BMS00_T2 "BMS_00 temperature T2 [%.1f °C]" (jkbms) { channel="http:url:ibms:bms00_temperature_sensor_2" }
Number:Temperature BMS00_T3 "BMS_00 temperature T3 [%.1f °C]" (jkbms) { channel="http:url:ibms:bms00_temperature_sensor_3" }
Number:Temperature BMS00_T4 "BMS_00 temperature T4 [%.1f °C]" (jkbms) { channel="http:url:ibms:bms00_temperature_sensor_4" }
// Once again, rinse and repeat for BMS01 and BMS02.
There you go. Now you have all the individual data points in your openHAB and can monitor, set alarms, export to grafana and so on. Btw, this took less time to whip together than the time it took to write up this post with a little help from ChatGPT to go from api/JSON data to .things to .items ;).