initialize

This commit is contained in:
2026-04-24 16:34:10 -04:00
commit 9aca623336
202 changed files with 6718 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
# HA-side configuration for the eg4-battery daemon
Reference configs that go into your Home Assistant instance — they aren't
installed by `install.sh` (HA typically lives on a different host / in an
HA OS appliance), but they're tracked here so the full stack is reproducible.
## What's in here
| File | Where it goes in HA |
|---------------------------|-----------------------------------------------------|
| `recorder.yaml` | `configuration.yaml` → under a `recorder:` key, or merge into existing |
| `template_sensors.yaml` | `configuration.yaml` → under a top-level `template:` list, or include via `!include` |
| `lovelace_overview.yaml` | Raw Lovelace card config — paste into a new dashboard view |
All of it assumes pack names `lifepower4_1`, `lifepower4_2`, `lifepower4_3`
matching the daemon's default config. If you renamed your packs, do a
`sed -i 's/lifepower4_/your_prefix_/' *.yaml` first.
## Recommended retention tiers
Full rationale in [`../NOTES.md`](../NOTES.md) and the architecture thread,
but the short version:
- **Tier 1 — keep forever**: `pack_voltage`, `pack_current`, `soc`, `soh`,
`cycle_count`, `cell_voltage_min/max/delta_mv`, `capacity_ah`.
- **Tier 2 — keep short**: all 14 `warning_*` + 14 `protection_*`,
`error_code`, `remaining_ah`, `heater`, the derived `temperature_max`
and `pack_power`.
- **Tier 3 — exclude** (the `recorder.yaml` here does this): all 47 raw
`register_NN` entities, the 16 individual `cell_NN_voltage` series,
static metadata (`bms_version_*`, `battery_mode`, `cell_count`, etc.),
and the `uptime_ds` counter that increments every second.
## Enabling in HA
Easiest path:
```yaml
# configuration.yaml
# merge our recorder exclusions with your existing recorder config
recorder: !include eg4_battery/recorder.yaml
# include the template sensors (creates a new `template:` list block)
template: !include eg4_battery/template_sensors.yaml
```
And drop the two YAMLs into `~/homeassistant/eg4_battery/`.
If you already have `recorder:` or `template:` keys elsewhere, merge by
hand — HA doesn't allow two definitions of the same top-level key.
## Energy dashboard wiring (optional)
Once the derived `pack_power` template sensors exist, add them to the
Energy dashboard via **Settings → Dashboards → Energy → Home battery
storage** — each pack's `pack_power` integrates to `pack_energy_in_kwh`
and `pack_energy_out_kwh` automatically, with per-pack bars.

View File

@@ -0,0 +1,116 @@
# Lovelace card config for a 3-pack LifePower4 stack overview.
# Paste into a dashboard view's raw-config editor, or drop in as a YAML-mode
# dashboard. Assumes the template sensors in template_sensors.yaml exist.
views:
- title: Battery Stack
icon: mdi:battery
path: batteries
cards:
# ---- stack summary row ----
- type: horizontal-stack
cards:
- type: gauge
name: Stack SoC
entity: sensor.lifepower4_stack_soc_avg
min: 0
max: 100
severity:
green: 40
yellow: 20
red: 0
- type: entity
name: Stack Power
entity: sensor.lifepower4_stack_pack_power_total
icon: mdi:flash
- type: entity
name: Hottest Point
entity: sensor.lifepower4_stack_temperature_max
icon: mdi:thermometer
# ---- per-pack summary cards ----
- type: horizontal-stack
cards:
- !include_named pack_summary_pack1.yaml
- !include_named pack_summary_pack2.yaml
- !include_named pack_summary_pack3.yaml
# ---- pack voltage time series ----
- type: history-graph
title: Pack voltage (24 h)
hours_to_show: 24
entities:
- sensor.lifepower4_1_pack_voltage
- sensor.lifepower4_2_pack_voltage
- sensor.lifepower4_3_pack_voltage
# ---- SoC + SoH trend ----
- type: history-graph
title: SoC / SoH
hours_to_show: 168 # 1 week
entities:
- sensor.lifepower4_1_soc
- sensor.lifepower4_2_soc
- sensor.lifepower4_3_soc
- sensor.lifepower4_1_soh
- sensor.lifepower4_2_soh
- sensor.lifepower4_3_soh
# ---- cell balance health (the crucial long-term metric) ----
- type: history-graph
title: Cell voltage delta (mV) — rising = balance degrading
hours_to_show: 168
entities:
- sensor.lifepower4_1_cell_voltage_delta_mv
- sensor.lifepower4_2_cell_voltage_delta_mv
- sensor.lifepower4_3_cell_voltage_delta_mv
# ---- any active warnings/protections — glance card, visible red when on ----
- type: entities
title: Alarms (should all be "off" on a healthy stack)
show_header_toggle: false
entities:
- type: section
label: Pack 1
- entity: sensor.lifepower4_1_warning_cell_ov
name: Cell OV
- entity: sensor.lifepower4_1_warning_cell_uv
name: Cell UV
- entity: sensor.lifepower4_1_warning_charge_oc
name: Charge OC
- entity: sensor.lifepower4_1_warning_discharge_oc
name: Discharge OC
- entity: sensor.lifepower4_1_warning_mos_ot
name: MOSFET OT
- entity: sensor.lifepower4_1_warning_low_capacity
name: Low Capacity
- entity: sensor.lifepower4_1_protection_cell_ov
name: PROT Cell OV
- entity: sensor.lifepower4_1_protection_cell_uv
name: PROT Cell UV
# (repeat structure for pack_2 and pack_3, omitted for brevity)
# Per-pack summary card template — save as three copies named
# pack_summary_pack1.yaml, pack_summary_pack2.yaml, pack_summary_pack3.yaml
# with only the N suffix different.
#
# type: entities
# title: Pack 1
# show_header_toggle: false
# entities:
# - entity: sensor.lifepower4_1_pack_voltage
# name: Voltage
# - entity: sensor.lifepower4_1_pack_current
# name: Current
# - entity: sensor.lifepower4_1_pack_power
# name: Power
# - entity: sensor.lifepower4_1_soc
# name: SoC
# - entity: sensor.lifepower4_1_soh
# name: SoH
# - entity: sensor.lifepower4_1_temperature_max
# name: Hottest
# - entity: sensor.lifepower4_1_cell_voltage_delta_mv
# name: Cell Δ
# - entity: sensor.lifepower4_1_cycle_count
# name: Cycles

View File

@@ -0,0 +1,51 @@
# HA recorder exclusions for the eg4-battery daemon's MQTT entities.
#
# Merge with your existing recorder config; if you don't have one, this whole
# file can be referenced as `recorder: !include eg4_battery/recorder.yaml`.
#
# Rationale:
# - register_NN entities are raw Modbus registers, diagnostic only
# - individual cell voltages are redundant once you have min/max/delta
# - uptime / version / static config values are pure noise in a timeseries
#
# Everything NOT in `entity_globs` below keeps recording normally, including
# the Tier-1 (pack_voltage / soc / soh / cycle_count / cell_voltage_min/max/
# delta_mv / capacity_ah) and Tier-2 (warnings / protections / error_code)
# entities. See ../NOTES.md for the retention-tier breakdown.
exclude:
entity_globs:
# raw Modbus register dump — diagnostic only
- sensor.lifepower4_*_register_*
# 16 individual cells per pack = 48 noisy series.
# cell_voltage_min / _max / _delta_mv already capture 95% of the info.
# Comment this out if you're debugging a specific drifting cell.
- sensor.lifepower4_*_cell_01_voltage
- sensor.lifepower4_*_cell_02_voltage
- sensor.lifepower4_*_cell_03_voltage
- sensor.lifepower4_*_cell_04_voltage
- sensor.lifepower4_*_cell_05_voltage
- sensor.lifepower4_*_cell_06_voltage
- sensor.lifepower4_*_cell_07_voltage
- sensor.lifepower4_*_cell_08_voltage
- sensor.lifepower4_*_cell_09_voltage
- sensor.lifepower4_*_cell_10_voltage
- sensor.lifepower4_*_cell_11_voltage
- sensor.lifepower4_*_cell_12_voltage
- sensor.lifepower4_*_cell_13_voltage
- sensor.lifepower4_*_cell_14_voltage
- sensor.lifepower4_*_cell_15_voltage
- sensor.lifepower4_*_cell_16_voltage
# static metadata (doesn't change, no reason to keep history)
- sensor.lifepower4_*_bms_version_hi
- sensor.lifepower4_*_bms_version_lo
- sensor.lifepower4_*_cell_count
- sensor.lifepower4_*_cell_highest
- sensor.lifepower4_*_cell_lowest
- sensor.lifepower4_*_battery_mode
- sensor.lifepower4_*_max_current_limit
# uptime counter — increments every second, kills the recorder's write cache
- sensor.lifepower4_*_uptime_ds

View File

@@ -0,0 +1,174 @@
# Derived template sensors for the eg4-battery daemon's 3-pack stack.
# Include into configuration.yaml as:
# template: !include eg4_battery/template_sensors.yaml
#
# Per-pack entities created:
# sensor.lifepower4_N_pack_power W (V × I, signed; + = charging)
# sensor.lifepower4_N_temperature_max °C (max of 5 temp sensors)
# sensor.lifepower4_N_cell_imbalance_pct % (delta / min_cell) × 100
#
# Stack-wide rollups:
# sensor.lifepower4_stack_pack_power_total W (sum of all 3 pack_powers)
# sensor.lifepower4_stack_soc_avg % (average SoC across packs)
# sensor.lifepower4_stack_temperature_max °C (hottest point anywhere)
- sensor:
# ---- pack 1 ----
- name: "lifepower4_1 pack_power"
unique_id: lifepower4_1_pack_power
unit_of_measurement: "W"
device_class: power
state_class: measurement
state: >
{% set v = states('sensor.lifepower4_1_pack_voltage') | float(0) %}
{% set i = states('sensor.lifepower4_1_pack_current') | float(0) %}
{{ (v * i) | round(1) }}
- name: "lifepower4_1 temperature_max"
unique_id: lifepower4_1_temperature_max
unit_of_measurement: "°C"
device_class: temperature
state_class: measurement
state: >
{% set t = [
states('sensor.lifepower4_1_temperature_01') | int(0),
states('sensor.lifepower4_1_temperature_02') | int(0),
states('sensor.lifepower4_1_temperature_03') | int(0),
states('sensor.lifepower4_1_temperature_04') | int(0),
states('sensor.lifepower4_1_temperature_pcb') | int(0),
] %}
{{ t | max }}
- name: "lifepower4_1 cell_imbalance_pct"
unique_id: lifepower4_1_cell_imbalance_pct
unit_of_measurement: "%"
state_class: measurement
state: >
{% set d = states('sensor.lifepower4_1_cell_voltage_delta_mv') | float(0) %}
{% set mn = states('sensor.lifepower4_1_cell_voltage_min') | float(0) %}
{{ (d / (mn * 1000) * 100) | round(3) if mn > 0 else 0 }}
# ---- pack 2 ----
- name: "lifepower4_2 pack_power"
unique_id: lifepower4_2_pack_power
unit_of_measurement: "W"
device_class: power
state_class: measurement
state: >
{% set v = states('sensor.lifepower4_2_pack_voltage') | float(0) %}
{% set i = states('sensor.lifepower4_2_pack_current') | float(0) %}
{{ (v * i) | round(1) }}
- name: "lifepower4_2 temperature_max"
unique_id: lifepower4_2_temperature_max
unit_of_measurement: "°C"
device_class: temperature
state_class: measurement
state: >
{% set t = [
states('sensor.lifepower4_2_temperature_01') | int(0),
states('sensor.lifepower4_2_temperature_02') | int(0),
states('sensor.lifepower4_2_temperature_03') | int(0),
states('sensor.lifepower4_2_temperature_04') | int(0),
states('sensor.lifepower4_2_temperature_pcb') | int(0),
] %}
{{ t | max }}
- name: "lifepower4_2 cell_imbalance_pct"
unique_id: lifepower4_2_cell_imbalance_pct
unit_of_measurement: "%"
state_class: measurement
state: >
{% set d = states('sensor.lifepower4_2_cell_voltage_delta_mv') | float(0) %}
{% set mn = states('sensor.lifepower4_2_cell_voltage_min') | float(0) %}
{{ (d / (mn * 1000) * 100) | round(3) if mn > 0 else 0 }}
# ---- pack 3 ----
- name: "lifepower4_3 pack_power"
unique_id: lifepower4_3_pack_power
unit_of_measurement: "W"
device_class: power
state_class: measurement
state: >
{% set v = states('sensor.lifepower4_3_pack_voltage') | float(0) %}
{% set i = states('sensor.lifepower4_3_pack_current') | float(0) %}
{{ (v * i) | round(1) }}
- name: "lifepower4_3 temperature_max"
unique_id: lifepower4_3_temperature_max
unit_of_measurement: "°C"
device_class: temperature
state_class: measurement
state: >
{% set t = [
states('sensor.lifepower4_3_temperature_01') | int(0),
states('sensor.lifepower4_3_temperature_02') | int(0),
states('sensor.lifepower4_3_temperature_03') | int(0),
states('sensor.lifepower4_3_temperature_04') | int(0),
states('sensor.lifepower4_3_temperature_pcb') | int(0),
] %}
{{ t | max }}
- name: "lifepower4_3 cell_imbalance_pct"
unique_id: lifepower4_3_cell_imbalance_pct
unit_of_measurement: "%"
state_class: measurement
state: >
{% set d = states('sensor.lifepower4_3_cell_voltage_delta_mv') | float(0) %}
{% set mn = states('sensor.lifepower4_3_cell_voltage_min') | float(0) %}
{{ (d / (mn * 1000) * 100) | round(3) if mn > 0 else 0 }}
# ---- stack-wide rollups ----
- name: "lifepower4_stack pack_power_total"
unique_id: lifepower4_stack_pack_power_total
unit_of_measurement: "W"
device_class: power
state_class: measurement
state: >
{% set p = [
states('sensor.lifepower4_1_pack_power') | float(0),
states('sensor.lifepower4_2_pack_power') | float(0),
states('sensor.lifepower4_3_pack_power') | float(0),
] %}
{{ p | sum | round(1) }}
- name: "lifepower4_stack soc_avg"
unique_id: lifepower4_stack_soc_avg
unit_of_measurement: "%"
device_class: battery
state_class: measurement
state: >
{% set s = [
states('sensor.lifepower4_1_soc') | float(0),
states('sensor.lifepower4_2_soc') | float(0),
states('sensor.lifepower4_3_soc') | float(0),
] %}
{{ (s | sum / s | length) | round(1) }}
- name: "lifepower4_stack temperature_max"
unique_id: lifepower4_stack_temperature_max
unit_of_measurement: "°C"
device_class: temperature
state_class: measurement
state: >
{% set t = [
states('sensor.lifepower4_1_temperature_max') | int(0),
states('sensor.lifepower4_2_temperature_max') | int(0),
states('sensor.lifepower4_3_temperature_max') | int(0),
] %}
{{ t | max }}
# --- integration → energy (pack_power integrated to Wh) ---
# Paste this at the top level of configuration.yaml, NOT inside `template:`:
#
# sensor:
# - platform: integration
# source: sensor.lifepower4_1_pack_power
# name: lifepower4_1_pack_energy
# unit_prefix: k
# round: 3
# method: left
# # ... repeat for pack 2 and 3 ...
#
# Then wire the resulting sensor.lifepower4_N_pack_energy into the HA
# Energy dashboard → Home battery storage → one entry per pack.