Just in case anyone can use this, hopefully it will save someone some time. I picked up an Eco Worthy KP1100 Pure Sine Wave Off Grid inverter that has both Bluetooth and WiFi capabilities. I configured the device to use the WiFi via the app for remote access, but still wanted the data inside of Home Assistant.
Since the device publishes data via Bluetooth that flips between two byte arrays, I had to make a “raw” sensor that gets the data, reads the first byte and then publishes to other valid sensors. There likely is a more elegant method of handling this, but it works for me.
The data looks like this:
a1000000 65000000 00000301 03440003 0085ffff ffffffff 047e0000 00000000 02580425 ffff0000 0001ffff ffff0000 00010001 0002006e 00800096 00a0ffff ffffffff ffffffff ffffffff ffffffff ffff792f
and
a2000000 65000000 00000301 03560000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 1f43
The raw sensor gets the data and then I check for that first byte to see which set it is and act appropriately from there. There are still some bytes that may well have a function, but I have not seen them change, so am not pulling them. I think the A2 set may be generation history, but I haven’t run the inverter long, and no history shows in the app yet, so not sure at this point.
Only the additional needed sections are included below. Build out your base node first, or add this to an existing Bluetooth ESP32 node you already have running. I run this on top of the Eco-Worthy battery monitor without issues.
Note that the “notify” option has to be turned on in the raw sensor for this to function.
There are two switches on the device for eco mode and on/off, but I have not decoded how to set them up as switches in HA.
...
ble_client:
- id: inverter
mac_address: XX:XX:XX:X:XX:XX # Edit for your mac
…
binary_sensor:
- platform: template
name: "KP1100 Inverter Status"
id: inverter_status
- platform: template
name: "KP1100 Inverter EcoMode"
id: inverter_ecomode
sensor:
# KP1100 Inveter Data
- platform: template
name: "KP1100 Inverter AC Volts"
id: inverter_ac_volts
unit_of_measurement: "v"
accuracy_decimals: 1
icon: "mdi:power-socket-us"
- platform: template
name: "KP1100 Inverter AC Current"
id: inverter_ac_current
unit_of_measurement: "a"
accuracy_decimals: 1
icon: "mdi:power-socket-us"
- platform: template
name: "KP1100 Inverter AC Watts"
id: inverter_ac_watts
unit_of_measurement: "w"
accuracy_decimals: 1
icon: "mdi:power-socket-us"
- platform: template
name: "KP1100 Inverter AC Freq"
id: inverter_ac_freq
unit_of_measurement: "hz"
accuracy_decimals: 1
icon: "mdi:power-socket-us"
- platform: template
name: "KP1100 Inverter OverDisCharge Cutoff Volts"
id: inverter_overdischarge_cutoff_volts
unit_of_measurement: "v"
accuracy_decimals: 1
icon: "mdi:battery"
- platform: template
name: "KP1100 Inverter OverDisCharge Recovery Volts"
id: inverter_overdischarge_recovery_volts
unit_of_measurement: "v"
accuracy_decimals: 1
icon: "mdi:battery"
- platform: template
name: "KP1100 Inverter OverCharge Recovery Volts"
id: inverter_overcharge_recovery_volts
unit_of_measurement: "v"
accuracy_decimals: 1
icon: "mdi:battery"
- platform: template
name: "KP1100 Inverter OverCharge Cutoff Volts"
id: inverter_overcharge_cutoff_volts
unit_of_measurement: "v"
accuracy_decimals: 1
icon: "mdi:battery"
- platform: template
name: "KP1100 Inverter Battery Volts"
id: inverter_battery_volts
unit_of_measurement: "v"
accuracy_decimals: 1
icon: "mdi:battery"
- platform: template
name: "KP1100 Inverter Temperature"
id: inverter_temperature
unit_of_measurement: "f"
accuracy_decimals: 1
- platform: ble_client
ble_client_id: inverter
internal: true
name: "KP1100 Inverter Raw Data"
type: characteristic
service_uuid: 'fff0'
characteristic_uuid: 'fff1'
id: inverter_raw_data
notify: true
lambda: |-
if (x[0] == 161)
{
id(inverter_battery_volts).publish_state((float) encode_uint16(x[16], x[17])/10);
id(inverter_ac_volts).publish_state((float) encode_uint16(x[24], x[25])/10);
id(inverter_ac_current).publish_state((float) encode_uint16(x[26], x[27])/100);
id(inverter_ac_watts).publish_state((float) encode_uint16(x[28], x[29])/10);
id(inverter_ac_freq).publish_state((float) encode_uint16(x[32], x[33])/10);
id(inverter_temperature).publish_state((float) encode_uint16(x[34], x[35])/10);
id(inverter_overdischarge_cutoff_volts).publish_state((float) encode_uint16(x[54], x[55])/10);
id(inverter_overdischarge_recovery_volts).publish_state((float) encode_uint16(x[56], x[57])/10);
id(inverter_overcharge_recovery_volts).publish_state((float) encode_uint16(x[58], x[59])/10);
id(inverter_overcharge_cutoff_volts).publish_state((float) encode_uint16(x[60], x[61])/10);
if (x[49] == 1)
{
id(inverter_status).publish_state(true);
}
else
{
id(inverter_status).publish_state(false);
}
if (x[47] == 1)
{
id(inverter_ecomode).publish_state(true);
}
else
{
id(inverter_ecomode).publish_state(false);
}
if (x[53] == 0)
{
id(inverter_battery_type).publish_state("Custom Battery");
}
if (x[53] == 1)
{
id(inverter_battery_type).publish_state("Lead-Acid Battery");
}
if (x[53] == 2)
{
id(inverter_battery_type).publish_state("LiFePo4 Battery");
}
}
// Handle any secondary data (xA2) if desired
if (x[0] == 162)
{
}
// Have to return something here or it doesn't work apparently
return 0.0;
text_sensor:
- platform: template
name: "KP1100 Inverter Battery Type"
id: inverter_battery_type
...