substitutions:
name: jk-bms
friendly_name: JK BMS # Numele afișat în Home Assistant
device_description: "JK BMS - Generic Configuration"
mac_address: AA:BB:CC:DD:EE:FF # Adresa MAC BLE a BMS-ului (verifică în app-ul JK)
protocol_version: JK02_32S
bms_name: "Battery"
esphome:
name: ${name}
friendly_name: ${friendly_name}
esp32:
board: esp32dev
framework:
type: esp-idf
logger:
level: WARN
api:
encryption:
key: "YOUR_32_BYTE_BASE64_KEY_HERE"
reboot_timeout: 0s
wifi:
ssid: "YOUR_WIFI_SSID" # Numele rețelei WiFi
password: "YOUR_WIFI_PASSWORD" # Parola WiFi
use_address: 192.168.1.100 # IP-ul pe care îl vei accesa în browser
power_save_mode: none
fast_connect: on
manual_ip:
static_ip: 192.168.1.100 # IP fix pentru ESP32
gateway: 192.168.1.1 # IP-ul router-ului tău
subnet: 255.255.255.0
dns1: 1.1.1.1
web_server:
local: true
port: 80
version: 2
include_internal: false
auth:
username: "admin"
password: "YOUR_WEB_PASSWORD"
ota:
platform: esphome
password: "YOUR_OTA_PASSWORD"
external_components:
- source: github://syssi/esphome-jk-bms@main
refresh: 5s
time:
- platform: homeassistant
id: homeassistant_time
timezone: Europe/Bucharest
on_time_sync:
then:
- logger.log: "Ora sincronizata cu Home Assistant"
- platform: sntp
id: sntp_time
timezone: Europe/Bucharest
servers:
- time.google.com
- 0.pool.ntp.org
- 1.pool.ntp.org
on_time_sync:
then:
- logger.log: "Ora sincronizata cu SNTP (backup)"
esp32_ble_tracker:
on_ble_advertise:
then:
- lambda: |-
if (x.get_name().rfind("JK-", 0) == 0) {
ESP_LOGI("ble_adv", "JK-BMS detectat: %s [%s]", x.get_name().c_str(), x.address_str().c_str());
}
ble_client:
- mac_address: ${mac_address}
id: client0
jk_bms_ble:
- ble_client_id: client0
protocol_version: ${protocol_version}
throttle: 30s
id: bms0
globals:
- id: wifi_retry_count
type: int
restore_value: no
initial_value: '0'
- id: last_ble_update
type: unsigned long
restore_value: no
initial_value: '0'
binary_sensor:
- platform: template
name: "wifi connected"
id: wifi_connected
device_class: connectivity
entity_category: diagnostic
lambda: |-
return wifi::global_wifi_component->is_connected();
- platform: template
name: "${bms_name} ble alive"
id: ble_alive
device_class: connectivity
entity_category: diagnostic
lambda: |-
unsigned long now = millis();
unsigned long last = id(last_ble_update);
bool voltage_valid = !isnan(id(batt_voltage).state);
bool current_valid = !isnan(id(batt_current).state);
bool soc_valid = !isnan(id(batt_soc).state);
int valid_count = (voltage_valid ? 1 : 0) + (current_valid ? 1 : 0) + (soc_valid ? 1 : 0);
if (valid_count >= 2) {
id(last_ble_update) = now;
return true;
}
if (last > 0 && (now - last) > 300000) {
ESP_LOGW("watchdog", "${bms_name} BLE NU COMUNICA de 5 minute!");
return false;
}
return (last > 0);
- platform: jk_bms_ble
jk_bms_ble_id: bms0
balancing:
name: "${bms_name} balancing"
charging:
name: "${bms_name} charging"
discharging:
name: "${bms_name} discharging"
online_status:
name: "${bms_name} online status"
sensor:
- platform: wifi_signal
name: "wifi signal db"
id: wifi_signal_db
update_interval: 60s
entity_category: diagnostic
internal: true
- platform: template
name: "wifi signal percent"
unit_of_measurement: "%"
icon: "mdi:wifi"
update_interval: 60s
accuracy_decimals: 0
entity_category: diagnostic
lambda: |-
float dbm = id(wifi_signal_db).state;
if (isnan(dbm)) return 0;
if (dbm <= -100) return 0;
if (dbm >= -50) return 100;
return 2 * (dbm + 100);
- platform: jk_bms_ble
jk_bms_ble_id: bms0
min_cell_voltage:
name: "${bms_name} min cell voltage"
max_cell_voltage:
name: "${bms_name} max cell voltage"
min_voltage_cell:
name: "${bms_name} min voltage cell"
max_voltage_cell:
name: "${bms_name} max voltage cell"
delta_cell_voltage:
name: "${bms_name} delta cell voltage"
average_cell_voltage:
name: "${bms_name} average cell voltage"
cell_voltage_1:
name: "${bms_name} cell 1"
cell_voltage_2:
name: "${bms_name} cell 2"
cell_voltage_3:
name: "${bms_name} cell 3"
cell_voltage_4:
name: "${bms_name} cell 4"
cell_voltage_5:
name: "${bms_name} cell 5"
cell_voltage_6:
name: "${bms_name} cell 6"
cell_voltage_7:
name: "${bms_name} cell 7"
cell_voltage_8:
name: "${bms_name} cell 8"
cell_voltage_9:
name: "${bms_name} cell 9"
cell_voltage_10:
name: "${bms_name} cell 10"
cell_voltage_11:
name: "${bms_name} cell 11"
cell_voltage_12:
name: "${bms_name} cell 12"
cell_voltage_13:
name: "${bms_name} cell 13"
cell_voltage_14:
name: "${bms_name} cell 14"
cell_voltage_15:
name: "${bms_name} cell 15"
cell_voltage_16:
name: "${bms_name} cell 16"
total_voltage:
name: "${bms_name} total voltage"
id: batt_voltage
current:
name: "${bms_name} current"
id: batt_current
power:
name: "${bms_name} power"
charging_power:
name: "${bms_name} charging power"
discharging_power:
name: "${bms_name} discharging power"
temperature_sensor_1:
name: "${bms_name} temperature 1"
temperature_sensor_2:
name: "${bms_name} temperature 2"
state_of_charge:
name: "${bms_name} SOC"
id: batt_soc
capacity_remaining:
name: "${bms_name} capacity remaining"
total_battery_capacity_setting:
name: "${bms_name} total battery capacity"
charging_cycles:
name: "${bms_name} charging cycles"
balancing_current:
name: "${bms_name} balancing current"
text_sensor:
- platform: template
name: "wifi status"
id: wifi_status_sensor
icon: "mdi:wifi"
update_interval: 60s
entity_category: diagnostic
lambda: |-
if (wifi::global_wifi_component->is_connected()) {
return {"WiFi OK"};
} else {
int count = id(wifi_retry_count);
char status[20];
sprintf(status, "Retry (%d/5)", count);
return {(std::string)status};
}
- platform: template
name: "esp time"
icon: "mdi:clock-outline"
update_interval: 1s
entity_category: diagnostic
lambda: |-
char time_str[20];
auto time = id(homeassistant_time).now();
if (time.is_valid()) {
sprintf(time_str, "%02d:%02d %02d.%02d.%04d",
time.hour, time.minute,
time.day_of_month, time.month, time.year);
return {(std::string)time_str};
}
return {"N/A"};
switch:
- platform: jk_bms_ble
jk_bms_ble_id: bms0
charging:
name: "${bms_name} charging"
discharging:
name: "${bms_name} discharging"
balancer:
name: "${bms_name} balancer"
- platform: ble_client
ble_client_id: client0
name: "${bms_name} BLE connection"
number:
- platform: jk_bms_ble
jk_bms_ble_id: bms0
max_charge_current:
name: "${bms_name} max charge current"
mode: box
max_discharge_current:
name: "${bms_name} max discharge current"
mode: box
balance_trigger_voltage:
name: "${bms_name} balance trigger voltage"
mode: box
balance_starting_voltage:
name: "${bms_name} balance starting voltage"
mode: box
cell_voltage_overvoltage_protection:
name: "${bms_name} overvoltage protection"
mode: box
cell_voltage_undervoltage_protection:
name: "${bms_name} undervoltage protection"
mode: box
power_off_voltage:
name: "${bms_name} power off voltage"
mode: box
interval:
- interval: 60s
then:
- lambda: |-
bool wifi_ok = wifi::global_wifi_component->is_connected();
if (wifi_ok) {
if (id(wifi_retry_count) != 0) {
ESP_LOGI("watchdog", "WiFi reconectat!");
}
id(wifi_retry_count) = 0;
} else {
id(wifi_retry_count) += 1;
int count = id(wifi_retry_count);
ESP_LOGW("watchdog", "WiFi deconectat - Retry %d/5", count);
if (count >= 5) {
ESP_LOGW("watchdog", "=== WIFI DECONECTAT 5 MINUTE! RESTART! ===");
App.safe_reboot();
}
}
- interval: 60s
then:
- lambda: |-
unsigned long now = millis();
unsigned long last = id(last_ble_update);
if (last > 0 && (now - last) > 300000) {
ESP_LOGW("watchdog", "=== ${bms_name} BLE BLOCAT 5 MIN! RESTART! ===");
App.safe_reboot();
}
button:
- platform: restart
name: "restart"
icon: "mdi:restart"
entity_category: diagnostic
- platform: jk_bms_ble
jk_bms_ble_id: bms0
retrieve_settings:
name: "${bms_name} retrieve settings"
retrieve_device_info:
name: "${bms_name} retrieve device info"