- EMHASS config for Deye SUN-12K + 32 kWh battery, NordPool LV - Day-ahead optimization automation (14:30 daily) with real battery SOC init - Forecast vs reality template sensors and dashboard card - Migration log from Domoticz Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
306 lines
9.9 KiB
Markdown
306 lines
9.9 KiB
Markdown
# EMHASS Energy Management — Deye + NordPool Setup
|
||
|
||
Energy Management for Home Assistant (EMHASS) configured for:
|
||
- **Deye SUN-12K-SG04LP3-EU** hybrid inverter
|
||
- **32 kWh LiFePO4 battery**
|
||
- **NordPool LV** spot prices
|
||
- Self-consumption optimization with price arbitrage
|
||
|
||
---
|
||
|
||
## System Overview
|
||
|
||
```
|
||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||
│ Deye Inverter │ │ Home Assistant │ │ NordPool │
|
||
│ (MQTT → HA) │─────▶│ Sensors │◀─────│ Spot Prices │
|
||
│ │ │ │ │ (LV zone) │
|
||
└─────────────────┘ └────────┬────────┘ └─────────────────┘
|
||
│
|
||
▼
|
||
┌─────────────────┐
|
||
│ EMHASS │
|
||
│ (add-on) │
|
||
│ │
|
||
│ • Day-ahead │
|
||
│ optimization │
|
||
│ • Battery │
|
||
│ scheduling │
|
||
│ • Price │
|
||
│ arbitrage │
|
||
└────────┬────────┘
|
||
│
|
||
▼
|
||
┌─────────────────┐
|
||
│ Forecast Sensors │
|
||
│ • p_pv_forecast │
|
||
│ • p_load_forecast │
|
||
│ • p_grid_forecast│
|
||
│ • soc_batt_forecast│
|
||
└─────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## Daily Workflow
|
||
|
||
### 1. NordPool Price Publication (~14:30 EET/EEST)
|
||
|
||
NordPool publishes next-day spot prices for the LV zone. The `sensor.nordpool_kwh_lv_eur_3_10_021` sensor receives:
|
||
- `today` — 96 values (15-min resolution) for current day
|
||
- `tomorrow` — 96 values for next day (once published, typically by 14:30)
|
||
- `tomorrow_valid` — becomes `true` when prices are available
|
||
|
||
### 2. EMHASS Automation (14:30 daily)
|
||
|
||
```yaml
|
||
trigger: 14:30
|
||
↓
|
||
wait: tomorrow_valid == true (timeout 60min)
|
||
↓
|
||
build: 48-value price list (30-min resolution)
|
||
= NordPool 15-min prices resampled to 30-min steps
|
||
(prices already include distribution + VAT — no offset added)
|
||
read: battery_soc_init = sensor.deye_inverter_mqtt_battery_soc / 100
|
||
↓
|
||
POST: /action/dayahead-optim with price list + battery_soc_init
|
||
↓
|
||
wait: 15 seconds
|
||
↓
|
||
POST: /action/publish-data
|
||
↓
|
||
result: Forecast sensors updated with tomorrow's plan
|
||
```
|
||
|
||
### 3. What EMHASS Optimizes
|
||
|
||
With `costfun: profit` and `set_nodischarge_to_grid: true`:
|
||
- **Discharge battery** to cover load during expensive hours (sell to yourself)
|
||
- **Charge battery** from grid during cheap hours (price arbitrage)
|
||
- **Never export** to grid (`maximum_power_to_grid: 0`)
|
||
- Maintain SOC between 10%–100%, target 60% end-of-day
|
||
|
||
---
|
||
|
||
## Configuration Files
|
||
|
||
### EMHASS Config (`/addon_configs/5b918bf2_emhass/config.json`)
|
||
|
||
```json
|
||
{
|
||
"hass_url": "http://192.168.66.113:8123",
|
||
"long_lived_token": "<HA long-lived access token>",
|
||
|
||
"costfun": "profit",
|
||
"optimization_time_step": 30,
|
||
"historic_days_to_retrieve": 10,
|
||
|
||
"sensor_power_photovoltaics": "sensor.deye_inverter_mqtt_dc_total_power",
|
||
"sensor_power_load_no_var_loads": "sensor.deye_inverter_mqtt_total_load_power",
|
||
|
||
"maximum_power_from_grid": 16090,
|
||
"maximum_power_to_grid": 0,
|
||
|
||
"load_cost_forecast_method": "list",
|
||
|
||
"set_use_battery": true,
|
||
"battery_nominal_energy_capacity": 32000,
|
||
"battery_charge_power_max": 11000,
|
||
"battery_discharge_power_max": 11000,
|
||
"battery_charge_efficiency": 0.96,
|
||
"battery_discharge_efficiency": 0.96,
|
||
"battery_minimum_state_of_charge": 0.1,
|
||
"battery_maximum_state_of_charge": 1.0,
|
||
"battery_target_state_of_charge": 0.6,
|
||
|
||
"number_of_deferrable_loads": 0
|
||
}
|
||
```
|
||
|
||
### HA Configuration (`/config/configuration.yaml`)
|
||
|
||
```yaml
|
||
rest_command:
|
||
emhass_dayahead_optim:
|
||
url: http://192.168.66.113:5000/action/dayahead-optim
|
||
method: POST
|
||
content_type: application/json
|
||
body: "{{ body }}"
|
||
emhass_publish_data:
|
||
url: http://192.168.66.113:5000/action/publish-data
|
||
method: POST
|
||
content_type: application/json
|
||
```
|
||
|
||
### Automation (`/config/automations.yaml`)
|
||
|
||
Located at the end of automations.yaml — see the file for the full Jinja2 template that:
|
||
1. Reads `sensor.nordpool_kwh_lv_eur_3_10_021` `tomorrow` attribute
|
||
2. Resamples from 96 values (15-min) to 48 values (30-min)
|
||
3. Reads real battery SOC and converts to fraction (`battery_soc_init`)
|
||
4. Passes both to EMHASS via `load_cost_forecast` + `battery_soc_init` parameters
|
||
|
||
Note: NordPool prices already include distribution + VAT — no fixed offset is added.
|
||
|
||
---
|
||
|
||
## Key Sensors
|
||
|
||
| Role | Entity ID | Notes |
|
||
|------|-----------|-------|
|
||
| PV Power | `sensor.deye_inverter_mqtt_dc_total_power` | DC total from strings |
|
||
| Load Power | `sensor.deye_inverter_mqtt_total_load_power` | House consumption |
|
||
| Grid Power | `sensor.deye_inverter_mqtt_total_grid_power` | +import / -export |
|
||
| Battery SOC | `sensor.deye_inverter_mqtt_battery_soc` | % |
|
||
| Battery Power | `sensor.deye_inverter_mqtt_battery_power` | +discharge / -charge |
|
||
| NordPool | `sensor.nordpool_kwh_lv_eur_3_10_021` | Spot price EUR/kWh |
|
||
| PV Forecast | `sensor.p_pv_forecast` | EMHASS output |
|
||
| Load Forecast | `sensor.p_load_forecast` | EMHASS output |
|
||
| Grid Forecast | `sensor.p_grid_forecast` | EMHASS output |
|
||
| SOC Forecast | `sensor.soc_batt_forecast` | EMHASS output — planned end-of-day SOC (always = target 60%) |
|
||
|
||
---
|
||
|
||
## Sign Conventions
|
||
|
||
| Sensor | Positive | Negative |
|
||
|--------|----------|----------|
|
||
| `battery_power` | Discharging to load | Charging from PV/grid |
|
||
| `total_grid_power` | Importing from grid | Exporting to grid |
|
||
| `inverter_l*_power` | Export to grid | Import from grid |
|
||
|
||
---
|
||
|
||
## Comparison: Forecast vs Reality
|
||
|
||
### Template Sensors Created
|
||
|
||
| Sensor | Description |
|
||
|--------|-------------|
|
||
| `sensor.battery_soc_forecast_error` | Actual SOC - Forecast SOC (%) |
|
||
| `sensor.current_hour_price` | Current hour's electricity price (€/kWh) |
|
||
|
||
### Monitoring Dashboard
|
||
|
||
Add the dashboard card from `dashboard_card.yaml`:
|
||
1. Go to **Dashboard** → **Edit** → **Add Card** → **Manual**
|
||
2. Paste the YAML content
|
||
|
||
### Checking Performance
|
||
|
||
**Daily comparison questions:**
|
||
1. Did battery charge during cheap hours (00:00–05:00, 12:00–14:00)?
|
||
2. Did battery discharge during peak hours (19:00–22:00)?
|
||
3. Is `sensor.battery_soc_forecast_error` within ±5%?
|
||
|
||
**If error > 10%:**
|
||
- Check if Deye inverter is overriding EMHASS decisions
|
||
- Consider adding enforcement automation (see below)
|
||
|
||
### History Graph
|
||
|
||
Compare actual vs forecast over time:
|
||
```yaml
|
||
type: history-graph
|
||
entities:
|
||
- entity: sensor.deye_inverter_mqtt_battery_soc
|
||
name: Actual SOC
|
||
- entity: sensor.soc_batt_forecast
|
||
name: Forecast SOC
|
||
hours_to_show: 48
|
||
```
|
||
|
||
---
|
||
|
||
## Adding EV Charging (OpenEVSE)
|
||
|
||
When the EV charger is integrated:
|
||
|
||
1. **Create a sensor** for EV power (or use OpenEVSE integration):
|
||
```yaml
|
||
# In configuration.yaml
|
||
sensor:
|
||
- platform: template
|
||
sensors:
|
||
ev_charger_power:
|
||
friendly_name: "EV Charger Power"
|
||
value_template: "{{ states('sensor.openevse_current_power') | float(0) * 230 }}"
|
||
unit_of_measurement: "W"
|
||
```
|
||
|
||
2. **Create a template sensor** that subtracts EV from total load:
|
||
```yaml
|
||
- platform: template
|
||
sensors:
|
||
load_power_no_var_loads:
|
||
friendly_name: "Load Power (no EV)"
|
||
value_template: "{{ states('sensor.deye_inverter_mqtt_total_load_power') | float - states('sensor.ev_charger_power') | float(0) }}"
|
||
unit_of_measurement: "W"
|
||
```
|
||
|
||
3. **Update EMHASS config**:
|
||
```json
|
||
"sensor_power_load_no_var_loads": "sensor.load_power_no_var_loads",
|
||
"number_of_deferrable_loads": 1,
|
||
"nominal_power_of_deferrable_loads": [7400],
|
||
"operating_hours_of_each_deferrable_load": [6]
|
||
```
|
||
|
||
4. **Restart EMHASS**
|
||
|
||
---
|
||
|
||
## Troubleshooting
|
||
|
||
### EMHASS logs show "config.json does not exist"
|
||
|
||
The config file must be at:
|
||
```
|
||
/addon_configs/5b918bf2_emhass/config.json
|
||
```
|
||
|
||
Not at `/homeassistant/config.json` or `/config/config.json`.
|
||
|
||
### "Shape of passed values is (X, 1), indices imply (48, 1)"
|
||
|
||
Load sensor has insufficient history. EMHASS needs ≥48 data points (24h at 30-min resolution). Either:
|
||
- Wait for more history to accumulate
|
||
- Switch to `sensor.deye_inverter_mqtt_total_load_power` (has months of history)
|
||
|
||
### Check EMHASS status via SSH
|
||
|
||
```bash
|
||
ssh root@192.168.66.113
|
||
ha addons logs 5b918bf2_emhass | tail -30
|
||
ha addons restart 5b918bf2_emhass
|
||
```
|
||
|
||
### Verify NordPool prices are being passed
|
||
|
||
```bash
|
||
curl -s -X POST http://192.168.66.113:5000/action/dayahead-optim \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"load_cost_forecast": [0.20, 0.19, ...]}'
|
||
```
|
||
|
||
Check logs for: `Passed runtime parameters: {'load_cost_forecast': [...]}`
|
||
|
||
---
|
||
|
||
## Files in This Repository
|
||
|
||
| File | Purpose |
|
||
|------|---------|
|
||
| `config.json` | EMHASS configuration (deploy to HA) |
|
||
| `config_emhass.yaml` | Human-readable reference (same config) |
|
||
| `migration_log.md` | Migration notes from Domoticz |
|
||
| `README.md` | This file |
|
||
|
||
---
|
||
|
||
## References
|
||
|
||
- [EMHASS Documentation](https://emhass.readthedocs.io/)
|
||
- [EMHASS GitHub](https://github.com/davidusb-geek/emhass)
|
||
- [Deye Inverter MQTT Integration](https://github.com/kbialek/deye-inverter-mqtt)
|
||
- [NordPool Integration](https://www.home-assistant.io/integrations/nordpool/) |