pi ha-ev skill
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: homeassistant-ev
|
||||
description: "Edit and debug EV charging automations for the Renault R4 E-Tech in Home Assistant. Covers automations.yaml, get_ev_battery.py, entity IDs, the Renault API capabilities, charge scheduling logic, pricing integration, and VictoriaMetrics session logging. Any changes made to the EV automations or scripts must also be reflected in this skill file."
|
||||
description: "Edit and debug EV charging automations for the Renault R4 E-Tech in Home Assistant. Covers automations.yaml, scripts.yaml, get_ev_battery.py, entity IDs, the Renault API capabilities, price-based hour selection (dynamic window + plug-in trigger), and VictoriaMetrics session logging. Any changes made to the EV automations or scripts must also be reflected in this skill file."
|
||||
---
|
||||
|
||||
# Home Assistant EV Charging Automations
|
||||
@@ -12,205 +12,189 @@ Working directory: `/home/jonas/homeassistant/`
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `automations.yaml` | All automations including the full EV charging suite |
|
||||
| `get_ev_battery.py` | Two modes: poll current battery from Renault API; sync completed session data |
|
||||
| `configuration.yaml` | Defines `input_number`, `input_text`, `timer`, `shell_command` helpers |
|
||||
| `scripts.yaml` | `ev_calculate_charge_hours`, `ev_start_charge_session`, `ev_stop_charge_session`, `ev_toggle_charge` |
|
||||
| `get_ev_battery.py` | Poll battery from Renault API; manual adjustment-check helper |
|
||||
| `configuration.yaml` | Defines `input_number`, `input_text`, `timer`, `input_boolean` helpers |
|
||||
|
||||
## Architecture overview
|
||||
|
||||
The EV charger is a **Shelly Pro 1PM** smart switch (`switch.shellypro1pm_8c4f00b426d8`) that sits between the wall socket and the car's charging cable.
|
||||
The EV charger is a **Shelly Pro 1PM** smart switch (`switch.shellypro1pm_8c4f00b426d8`) that sits between the wall socket and the car's charging cable. No power = no charge; the car cannot signal anything when the relay is off.
|
||||
|
||||
**Important limitations of the Renault API:**
|
||||
- `battery-status.batteryLevel` is arbitrarily stale (typically 10–30 min old)
|
||||
- The `timestamp` field tells you when the car last reported, but there is no way to know the current battery level in real time
|
||||
- `sensor.shellypro1pm_8c4f00b426d8_power` does NOT reflect what the car draws — the car controls its own intake independently
|
||||
- There is **no charge cap / target SOC API** in renault-api for this model
|
||||
- `sensor.r4_e_tech_battery` / `battery-status.batteryLevel` is arbitrarily stale (typically 10–30 min old)
|
||||
- There is **no charge cap / target SOC API** for this model
|
||||
- `sensor.shellypro1pm_8c4f00b426d8_power` does NOT reflect what the car draws
|
||||
- Cloud entities (`binary_sensor.r4_e_tech_plug`) can lag a few minutes behind reality
|
||||
|
||||
**Consequence:** mid-session polling cannot give reliable trajectory data. The stop mechanism is purely **time-based**.
|
||||
## Scheduling: dynamic cheapest-hours window (2026-08 change)
|
||||
|
||||
## Stop mechanism: time-based timer with two-zone charging curve
|
||||
`script.ev_calculate_charge_hours` computes needed hours from battery/target/rate and picks the cheapest N hours from a **dynamic window: all remaining hours from now() until 06:00 next morning**.
|
||||
|
||||
AC home charging is not linear. The R4 E-Tech charges at roughly **10 kW below 80% SOC** and tapers to around **3.8–5 kW above 80%**. A single average rate would overestimate duration for sessions that don't cross 80% (causing early stop), and underestimate for sessions that do (causing overshoot).
|
||||
- Before midnight: today's hours `now().hour..23` plus tomorrow `0–5`
|
||||
- After midnight: today's remaining `0–5` hours only
|
||||
- Tomorrow's hours are only considered when `raw_tomorrow` price data actually exists (Energi Data Service publishes day-ahead prices ~13:00), so unpriced hours never win on the 0.0 fallback
|
||||
- The current (partial) hour is included, so a mid-hour plug-in can start charging immediately if that hour is cheapest
|
||||
- Result stored as JSON array of hour integers in `input_text.ev_charge_hours`
|
||||
|
||||
Instead, duration is computed with a two-zone formula:
|
||||
Selected hours are purely price-driven. Cheap afternoon/evening hours win over night hours whenever the data says so (DK2 prices shift with solar/wind).
|
||||
|
||||
## Charging curve model
|
||||
|
||||
AC charging tapers above 80% SOC. Model:
|
||||
|
||||
```
|
||||
total_min = pct_below_80 * mpp_below + pct_above_80 * mpp_above
|
||||
mpp(soc) = mpp_below * exp(0.07 * (soc - 80)) # MPP_K = 0.07
|
||||
total_min ≈ pct_below_80 * mpp_below + pct_above_80 * mpp_below * 2.0
|
||||
```
|
||||
|
||||
Where `pct_below_80 = min(80, target) - start` and `pct_above_80 = max(0, target - 80)`.
|
||||
Only one fitted parameter exists as an entity:
|
||||
- `input_number.ev_min_per_pct_below_80` — minutes per % below 80% (range 1.5–3.5, default 2.2)
|
||||
|
||||
The two `min/%` values are stored in:
|
||||
- `input_number.ev_min_per_pct_below_80` — EMA-fitted from sessions entirely or mostly below 80%
|
||||
- `input_number.ev_min_per_pct_above_80` — derived from sessions that cross 80%, using the below-80 rate to split session time
|
||||
Above 80% the scripts use a fixed `2.0 × mpp_below` approximation. There is no separate `mpp_above` entity and no automated session-history refitting in the current setup (the old `--sync-sessions` mode was removed from `get_ev_battery.py`). Adjust `ev_min_per_pct_below_80` manually if sessions consistently end early/late.
|
||||
|
||||
Both are updated after every session via `get_ev_battery.py --sync-sessions`, which re-fits the model deterministically from all available sessions in the last 60 days (starting from neutral seeds, not stored values, so the result is always idempotent).
|
||||
|
||||
`timer.ev_charge_session` is set to this duration and is the **primary stop signal**. When it fires, the Shelly is turned off and a Renault cloud charge-stop is sent.
|
||||
|
||||
A **single mid-session sanity check** fires at the halfway point (`timer.ev_charge_poll`). If the API reading at that point already confirms ≥ target, it stops early and cancels the session timer. Otherwise it does nothing — the session timer continues.
|
||||
|
||||
## Rate calibration: BMS session data
|
||||
|
||||
After the Shelly turns off, `timer.ev_post_charge_sync` starts a 30-minute delay. When it fires, `get_ev_battery.py --sync-sessions` queries:
|
||||
|
||||
```bash
|
||||
renault-api charge sessions --from YYYY-MM-DD --to YYYY-MM-DD
|
||||
```
|
||||
|
||||
This returns the car's BMS-recorded sessions with exact `chargeStartDate`, `chargeEndDate`, `chargeStartBatteryLevel`, `chargeEndBatteryLevel`, and `chargeEnergyRecovered` (kWh).
|
||||
|
||||
Duration is computed from the timestamps (not from the `chargeDuration` field, which has display issues). Rate = `energy_kwh / duration_hours`. This is updated into `input_number.ev_charging_power_kw` as a rolling average of the last 3 sessions and logged to VictoriaMetrics as `ev_session`.
|
||||
`timer.ev_charge_session` is set to this duration and is the primary stop signal.
|
||||
|
||||
## Helper entities (defined in configuration.yaml)
|
||||
|
||||
| Entity | Type | Purpose |
|
||||
|---|---|---|
|
||||
| `input_number.ev_target_charge` | number (50–100, step 5) | Target SOC%, default 80 |
|
||||
| `input_number.ev_charging_power_kw` | number (1–22, step 0.1) | Below-80% rate in kW implied by mpp_below (display/fallback) |
|
||||
| `input_number.ev_min_per_pct_below_80` | number (0.5–15, step 0.01) | Minutes per % SOC for charging below 80%; fitted from BMS sessions |
|
||||
| `input_number.ev_min_per_pct_above_80` | number (0.5–30, step 0.01) | Minutes per % SOC for charging above 80%; derived from crossing sessions |
|
||||
| `input_number.ev_real_battery` | number (0–100) | Raw API battery % (updated by `get_ev_battery.py` poll) |
|
||||
| `input_number.ev_target_charge` | number (50–100, step 5) | Target SOC%, default 80, reset to 80 daily at 06:00 |
|
||||
| `input_number.ev_charging_power_kw` | number (1–22) | Display/fallback rate in kW |
|
||||
| `input_number.ev_min_per_pct_below_80` | number (1.5–3.5, mode box) | Minutes per % below 80%; core model parameter |
|
||||
| `input_number.ev_charge_session_start_battery` | number | SOC% at session start |
|
||||
| `input_text.ev_charge_hours` | text | JSON array of planned charge hours, e.g. `[22, 0, 1]` |
|
||||
| `input_text.ev_charge_session_start_ts` | text | Unix timestamp of session start |
|
||||
| `input_text.ev_charge_rates` | text | JSON array of last 3 BMS-measured rates in kW |
|
||||
| `timer.ev_charge_session` | timer | Primary stop: fires after calculated charge duration |
|
||||
| `timer.ev_charge_poll` | timer | One-shot sanity check at session midpoint |
|
||||
| `timer.ev_post_charge_sync` | timer | 30-min post-session delay before querying BMS data |
|
||||
| `timer.ev_charge_session` | timer (restore: true) | Primary stop: fires after calculated duration |
|
||||
| `timer.ev_post_charge_check` | timer (restore: true) | 10-min delay before adjustment check |
|
||||
| `timer.ev_charge_adjustment` | timer (restore: true) | Post-charge top-up session duration |
|
||||
| `input_boolean.ev_charge_session_active` | flag | A main/adjustment session is running |
|
||||
| `input_boolean.ev_adjustment_mode` | flag | Current session is an adjustment top-up |
|
||||
|
||||
## Automation IDs and what they do
|
||||
## Automations
|
||||
|
||||
### `ev_calculate_charge_hours`
|
||||
- **Trigger:** Time `21:50:00`
|
||||
- **Action:** Reads spot prices (DK2, Energi Data Service), computes hours needed from battery/target/rate, picks cheapest N hours in the 22:00–05:00 window, writes to `input_text.ev_charge_hours`. Logs detail (prices, selection) and creates a persistent notification.
|
||||
### `ev_calculate_cheap_charge_hours` ("EV: Calculate cheap charge hours")
|
||||
- **Triggers:** time `21:50:00` (nightly fallback); `binary_sensor.r4_e_tech_plug` → `on` (lets cheap afternoon hours be used right away)
|
||||
- **Condition:** battery < target
|
||||
- **Actions:** run `script.ev_calculate_charge_hours`, post persistent notification with battery/target/hours/price availability, then `automation.trigger` on `automation.ev_hourly_charge_control` with `skip_condition: true` so the decision applies immediately (mid-hour start possible)
|
||||
- Also invoked by the dashboard RECALCULATE button and startup recovery
|
||||
|
||||
### `ev_hourly_charge_control`
|
||||
- **Triggers:** Time at 22:00, 23:00, 00:00, 01:00, 02:00, 03:00, 04:00, 05:00
|
||||
- **Action:** Turns Shelly ON if current hour is in the planned list AND `sensor.r4_e_tech_battery < target`; OFF otherwise. Logs decision with reason.
|
||||
### `ev_hourly_charge_control` ("EV: Hourly charge control")
|
||||
- **Trigger:** `time_pattern` every hour on the hour, all day
|
||||
- **Conditions:** `input_text.ev_charge_hours` not in `['[]', 'unknown', 'unavailable']` OR `input_boolean.ev_charge_session_active` on (prevents idle-daytime log spam)
|
||||
- **Actions:** if current hour planned AND battery < target → `script.ev_start_charge_session`; elif session active → Shelly off (logs reason); else log no-action
|
||||
|
||||
### `ev_startup_recovery`
|
||||
### `ev_startup_recovery` ("EV: Startup recovery")
|
||||
- **Trigger:** `homeassistant.start`
|
||||
- **Conditions:** `now().hour in [21,22,23,0,1,2,3,4,5]` AND battery < target
|
||||
- **Action:** Waits 2 min, recalculates charge hours, immediately applies the Shelly decision. Recovers from HA crashes during the charge window.
|
||||
- **Conditions:** `binary_sensor.r4_e_tech_plug` on AND battery < target (no time-of-day restriction anymore)
|
||||
- **Actions:** wait 2 min, recalc hours via script, notify, re-run hourly decision. Timers use restore: true so sessions survive restarts.
|
||||
|
||||
### `ev_charge_started` (alias: "EV: Start charge session")
|
||||
- **Trigger:** Shelly → `on`
|
||||
- **Action:** Polls API (raw battery → `ev_real_battery`), records session start. Sets:
|
||||
- `timer.ev_charge_session` → full calculated duration (primary stop)
|
||||
- `timer.ev_charge_poll` → half duration (sanity check)
|
||||
|
||||
### `ev_charge_poll_check` (alias: "EV: Mid-session sanity check")
|
||||
- **Trigger:** `timer.ev_charge_poll` finished
|
||||
- **Condition:** Shelly still `on`
|
||||
- **Action:** Polls API. If `ev_real_battery >= target`: cancels session timer, stops Shelly + Renault stop. Otherwise: logs reading only, leaves session timer running.
|
||||
|
||||
### `ev_session_time_reached` (alias: "EV: Session timer stop")
|
||||
### `ev_session_time_reached` ("EV: Session timer stop") — PRIMARY STOP
|
||||
- **Trigger:** `timer.ev_charge_session` finished
|
||||
- **Condition:** Shelly still `on`
|
||||
- **Action:** Turns off Shelly + presses `button.r4_e_tech_stop_charge`. This is the **primary stop path**.
|
||||
- **Condition:** Shelly still on
|
||||
- **Actions:** Shelly off + press `button.r4_e_tech_stop_charge`, then start `timer.ev_post_charge_check` (10 min)
|
||||
|
||||
### `ev_charge_stopped` (alias: "EV: Charge session ended")
|
||||
- **Trigger:** Shelly → `off`
|
||||
- **Action:** Cancels `timer.ev_charge_poll` and `timer.ev_charge_session` (whichever is still running). Starts `timer.ev_post_charge_sync` (30 min). Logs session start battery and duration.
|
||||
### `ev_post_charge_adjustment` ("EV: Post-charge adjustment check")
|
||||
- **Trigger:** `timer.ev_post_charge_check` finished
|
||||
- **Action:** if `sensor.r4_e_tech_battery` < target → `script.ev_start_charge_session` with `adjustment: true`; else log nothing needed. One adjustment per charging event by design of the flow.
|
||||
|
||||
### `ev_sync_session_data` (alias: "EV: Post-charge session sync")
|
||||
- **Trigger:** `timer.ev_post_charge_sync` finished
|
||||
- **Action:** Calls `shell_command.sync_ev_sessions` → `get_ev_battery.py --sync-sessions`. Logs updated rate.
|
||||
### `ev_adjustment_timer_stop` ("EV: Adjustment timer stop")
|
||||
- **Trigger:** `timer.ev_charge_adjustment` finished
|
||||
- **Condition:** Shelly on
|
||||
- **Actions:** Shelly off + Renault stop charge
|
||||
|
||||
### `ev_charge_stopped` ("EV: Charge session ended")
|
||||
- **Trigger:** Shelly → off (any cause: timers, hourly control, manual)
|
||||
- **Actions:** cancel both session timers, clear `ev_adjustment_mode` + `ev_charge_session_active`, log SESSION END only if a session was actually active
|
||||
|
||||
### `Shelly off` (id `1768306419435`)
|
||||
- **Trigger:** Time `06:00:00`
|
||||
- **Action:** Turns off Shelly, resets `ev_charge_hours` to `[]`, resets `ev_target_charge` to 80.
|
||||
- **Trigger:** time `06:00:00`
|
||||
- **Actions:** force Shelly off (backstop), reset `ev_target_charge` to 80, reset `ev_charge_hours` to `[]`
|
||||
|
||||
### Shelly remote/dashboard automations
|
||||
Plain ON/OFF button presses toggle the relay only — they do NOT start sessions. Sessions start exclusively through `script.ev_start_charge_session`. Long-press ON/OFF adds R4 AC start/cancel (guarded against cutting active charges).
|
||||
|
||||
## Scripts (scripts.yaml)
|
||||
|
||||
- `script.ev_calculate_charge_hours` — see scheduling section above
|
||||
- `script.ev_start_charge_session` — the ONLY session starter. If a session timer is already active it just re-asserts Shelly ON (prevents timer resets on consecutive planned hours). Otherwise records start battery/ts, sets flags, starts `timer.ev_charge_session` (or `timer.ev_charge_adjustment` when called with `adjustment: true`), turns Shelly on. Accepts field `adjustment: true|false`.
|
||||
- `script.ev_stop_charge_session` — Shelly off + Renault stop; cleanup happens in ev_charge_stopped
|
||||
- `script.ev_toggle_charge` — dashboard START/STOP button wrapper
|
||||
|
||||
## Shelly entity IDs (do not change)
|
||||
```
|
||||
device_id: e18cd73e8b837834b77acf81eca52224
|
||||
entity_id: 81ed4fbd80fee38a4817667cd8737748 (used in type: turn_on/off)
|
||||
switch.shellypro1pm_8c4f00b426d8 (used in state conditions and logbook)
|
||||
MQTT device_id (remote buttons): 286550d4f64f128d42e4dc338830f196
|
||||
```
|
||||
|
||||
## Renault entity IDs
|
||||
```
|
||||
sensor.r4_e_tech_battery — SOC% from HA integration (stale, for scheduling only)
|
||||
sensor.r4_e_tech_battery — SOC% (stale, for scheduling/adjustment decisions only)
|
||||
binary_sensor.r4_e_tech_plug — plug status (plug-in trigger; cloud lag possible)
|
||||
binary_sensor.r4_e_tech_charging — whether car reports actively charging
|
||||
button.r4_e_tech_stop_charge — sends charge-stop via Renault cloud
|
||||
button.r4_e_tech_start_charge — remote start (not currently used by automations)
|
||||
device_id: 5866f4d42ced21f6ce609e6b19d1ef65
|
||||
```
|
||||
|
||||
## Renault API capabilities
|
||||
|
||||
CLI: `~/.local/bin/renault-api`
|
||||
CLI: `renault-api` (see `get_ev_battery.py` docstring for details it relies on)
|
||||
|
||||
| Command | Status | Notes |
|
||||
|---|---|---|
|
||||
| `renault-api --json status` | ✅ | Returns `battery-status` with `timestamp`, `batteryLevel`, `chargingRemainingTime` |
|
||||
| `renault-api charge sessions --from DATE --to DATE` | ✅ | BMS-recorded sessions with start/end SOC and kWh. **Primary data source for rate calibration.** |
|
||||
| `renault-api charge stop` | ⚠️ | Model A4E1VE undocumented; may fail if not actively charging |
|
||||
| `renault-api charge mode` | ❌ | Access forbidden |
|
||||
| `renault-api charge schedule show/set` | ❌/⚠️ | show forbidden; set exists but is time+duration only, no SOC target |
|
||||
| `renault-api --json status` | ✅ | `battery-status` with stale `batteryLevel` + `timestamp` |
|
||||
| `renault-api charge stop` | ⚠️ | May fail if not actively charging |
|
||||
| `renault-api charge mode` / schedules | ❌ | Access forbidden |
|
||||
|
||||
**`chargeDuration` field display note:** the tabulated CLI output shows this in a misleading format. Always compute duration from `chargeStartDate` - `chargeEndDate` timestamps instead.
|
||||
## `get_ev_battery.py`
|
||||
|
||||
## `get_ev_battery.py` — two modes
|
||||
Auth: bearer token from `~/.ev_ha_token` (or `HA_BEARER_TOKEN` env). This token works for the HA REST API generally — usable to reload automations etc.:
|
||||
|
||||
**Poll mode (default):**
|
||||
- Calls `renault-api --json status`, extracts raw `batteryLevel` and `timestamp`
|
||||
- Updates `input_number.ev_real_battery` with the **raw API value** (no projection)
|
||||
- Logs to VM: `ev_charging` measurement with `battery_api_pct`, `api_age_seconds`, `target_pct`, `api_timestamp_unix`
|
||||
```bash
|
||||
curl -s -X POST -H "Authorization: Bearer $(cat ~/.ev_ha_token)" \
|
||||
-H "Content-Type: application/json" -d '{}' \
|
||||
http://localhost:8123/api/services/automation/reload
|
||||
```
|
||||
|
||||
**Sync mode (`--sync-sessions`):**
|
||||
- Calls `renault-api charge sessions --from <60 days ago> --to <today>`
|
||||
- Parses tabulated output: timestamps, start/end SOC%, energy kWh. Duration is always computed from `chargeStartDate`–`chargeEndDate` (the `chargeDuration` field has display issues).
|
||||
- Fits the two-zone model via chronological EMA starting from neutral seeds (3.0, 6.0):
|
||||
- Sessions entirely ≤80%: directly update `mpp_below`
|
||||
- Sessions crossing 80%: split time using current `mpp_below`, derive `mpp_above` from the above-80% portion
|
||||
- Sessions entirely ≥80%: directly update `mpp_above`
|
||||
- Discards sessions with `delta_pct < 3` or `duration < 2 min` as noise
|
||||
- Updates `input_number.ev_min_per_pct_below_80` and `input_number.ev_min_per_pct_above_80`
|
||||
- Updates `input_number.ev_charging_power_kw` (implied kW from mpp_below, for display)
|
||||
- Logs to VM: `ev_session` measurement with start/end %, duration, energy, rate, and new mpp values
|
||||
|
||||
**Note on crossing sessions with tiny above-80 range (e.g. 72→81%):** the 1% above 80% amplifies timing noise into large mpp estimates. These sessions are processed but have low signal for mpp_above. Sessions with pct_above ≥ 5% are more reliable.
|
||||
Modes:
|
||||
- default poll: calls `renault-api --json status`, writes raw battery % into `input_number.ev_real_battery` (note: this entity no longer exists in configuration.yaml — poll mode will fail at that step until recreated or the script is updated), logs to VictoriaMetrics
|
||||
- `--adjustment-check`: manual helper — reads sensor/target, turns Shelly on, sleeps the computed remaining minutes, turns off + stops charge. Standalone use only.
|
||||
|
||||
## VictoriaMetrics data
|
||||
|
||||
VM at `http://127.0.0.1:8428`. Written by `get_ev_battery.py` via InfluxDB line protocol.
|
||||
VM at `http://127.0.0.1:8428`. Written by `get_ev_battery.py` via InfluxDB line protocol (`ev_charging`, `ev_charging_adjustment` measurements). HA influxdb integration passively logs `sensor.r4_e_tech_battery`, `input_number.ev_charging_power_kw`.
|
||||
|
||||
| Measurement | When written | Key fields |
|
||||
|---|---|---|
|
||||
| `ev_charging` | Each poll (session start, sanity check) | `battery_api_pct`, `api_age_seconds` |
|
||||
| `ev_session` | 30 min after session end | `start_battery_pct`, `end_battery_pct`, `duration_minutes`, `energy_kwh`, `rate_kw` |
|
||||
|
||||
HA influxdb integration also passively logs `sensor.r4_e_tech_battery`, `input_number.ev_real_battery`, `input_number.ev_charging_power_kw` on state change.
|
||||
Query example:
|
||||
```bash
|
||||
curl -s "http://127.0.0.1:8428/api/v1/query_range?query=ev_charging&start=$(date -d '7 days ago' +%s)&end=$(date +%s)&step=3600"
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
**Check logbook:**
|
||||
**Check logbook entries (all EV logic logs under name "EV Charge Control"):**
|
||||
```bash
|
||||
podman logs homeassistant 2>&1 | grep "EV Charge Control"
|
||||
```
|
||||
|
||||
**Run scripts manually:**
|
||||
**Run poll manually:** `python3 /home/jonas/homeassistant/get_ev_battery.py`
|
||||
|
||||
**Reload after edits:**
|
||||
```bash
|
||||
python3 /home/jonas/homeassistant/get_ev_battery.py
|
||||
python3 /home/jonas/homeassistant/get_ev_battery.py --sync-sessions
|
||||
# automations + scripts (see curl snippet above for automation/reload)
|
||||
curl -s -X POST -H "Authorization: Bearer $(cat ~/.ev_ha_token)" -d '{}' \
|
||||
-H "Content-Type: application/json" http://localhost:8123/api/services/script/reload
|
||||
```
|
||||
Or Developer Tools → YAML → Reload Automations / Reload Scripts.
|
||||
|
||||
**Query VM session history:**
|
||||
```bash
|
||||
curl -s "http://127.0.0.1:8428/api/v1/query_range?query=ev_session_rate_kw&start=$(date -d '7 days ago' +%s)&end=$(date +%s)&step=3600"
|
||||
```
|
||||
**HA restarted while plugged in → ev_startup_recovery** fires 2 min after start (any time of day now).
|
||||
|
||||
**HA restarted during charge window → ev_startup_recovery** fires 2 min after start.
|
||||
**Automation didn't run** → check for HA restart: `podman logs homeassistant 2>&1 | head -5`
|
||||
|
||||
**ev_min_per_pct values are initial defaults** → run `python3 /home/jonas/homeassistant/get_ev_battery.py --sync-sessions` to fit from full history. The model is deterministic from session history so re-running is safe.
|
||||
**Testing templates without side effects:** POST the template JSON to `http://localhost:8123/api/template` — renders against real entity states without triggering anything.
|
||||
|
||||
**mpp_above is noisy if most sessions only go to 81%** → sessions with pct_above ≥ 5% give better signal. The model converges over time.
|
||||
## History notes
|
||||
|
||||
**Automation didn't run** → check for HA restart at `podman logs homeassistant 2>&1 | head -5`.
|
||||
|
||||
## How to edit the automations
|
||||
|
||||
1. Edit `/home/jonas/homeassistant/automations.yaml`
|
||||
2. Reload: `Developer Tools → YAML → Reload automations`
|
||||
3. For new entities: `Developer Tools → YAML → Reload input_number` / `Reload timer`
|
||||
4. **Always update this skill file** to reflect any logic, entity, or script changes
|
||||
- 2025 era: fixed nightly window (22:00–06:00) with mid-session polling timers — removed.
|
||||
- 2026-08: replaced fixed 22:00–06:00 selection window with the dynamic now()→06:00 window; added plug-in trigger on `binary_sensor.r4_e_tech_plug`; hourly control extended to all day (gated); startup recovery gated on plug instead of night hours; calculation now applies the decision immediately.
|
||||
|
||||
Reference in New Issue
Block a user